Every operational process at MakeHaven, in one place — what runs, what is broken, what is changing, and what should come next. Start with whichever question brought you here; everything else on this page is reference.
Recorded process states. These counts do not measure completed improvements or staff adoption.
The automated half of MakeHaven documents itself — where we can say which code does it. 53 processes run themselves, but only 33 name the module that runs them, and only those can claim the implementation is the description. The other 20 run as code nobody has pointed at. Meanwhile 112 take a human at every step, and 87 of those have nothing written beyond scattered notes. Only 1 process in 212 has documentation a second person has actually worked from — which is the real gap, and it applies to the code too.
This chart shows the recorded mix of process states. The count in flux (57) does not establish whether change is under control. Review the next actions, acceptance evidence and outstanding decisions with the people doing the work.
Read the bars as four separate problems, not one. 4 processes are broken — something that is supposed to happen does not, and that is the list to act on first. 23 are optimizable — they do what they were built to do and were never built to their end goal, which is ordinary unfinished work rather than a failure. That is a narrow word here, not a general one: nearly any process could be improved, but these are the ones with a defined end state nobody reached. 57 are in flux, and 17 are planned — intentions we have not started, where nothing exists to improve yet. Keeping these four apart is what makes the chart fair to read: collapsing them would show 27 things wrong when only 4 of them are actually broken.
The 57 processes that are not sitting still — what is mid-change, and what just landed and is still being watched.
20 processes reached stable or stopped being broken in the last 30 days. Read this list first: it is the point of all the others.
The standing answer to "what are we working on, and what has just landed?" — 16 actively changing and 41 recently changed and being watched. This is the list to put in front of a board or staff meeting.
optimizable — work is landing, not merely wished for. JR: "this is a weak point." Calls go to a Google voicemail box that often sits unanswered for a long time because of staff capacity. The voice AI routing track shipped its Phase 1 foundation live on 08-07, inert behind config; the Voicemail-Tool is written and pre-launch, awaiting a Twilio numberoptimizable rather than stable because the hand-off after the quiz was never finished, and that is where the flow leaks. Two concrete gaps. The member is never told how long a checkout takes: the time is recorded on 71 of the 73 checkout badges, averaging about 34 minutes, and the module exposes it as a [badge:checkout_minutes] token that its own shipped default template prints — but the customised template saved in assign_badge_from_quiz.settings dropped that line, so the one number a member needs to judge whether a checkout fits into today's visit is collected for almost every badge and shown for none. One line puts it back. (The checklist button beside it is a different matter: the template still handles it, but only 10 of 73 badges have a checklist URL to link to — missing data, not missing markup.) And nothing chases a badge that stalls. 5,107 badge requests sit PENDING on live. The pile is not the signal — 83% of it is over a year old and largely belongs to people who are no longer members — but the recent cohort is 80 requests from 49 members in the last 30 days, and nothing measures whether those convert, nudges the member, or would show the rate moving. The onboarding funnel got exactly that treatment once the same problem was measured there. What the panel does well is worth saying too: it shows a real timetable of who is in the building, with a Schedule button per shift and a card per facilitator. Walkthrough: https://claude.ai/code/artifact/1738fd87-b82c-4c86-8a86-76d50bf7bf18webform_10645 "Current Facilitator Application Update" shows two clear campaigns a year: Feb 2025 (40), Jul–Aug 2025 (33), Jan 2026 (31), Jul–Aug 2026 (38) — against 54 facilitators, roughly a 60–75 per cent response each cycle. The six-month term is real and is actually being run. What is missing is not the process but somewhere to put the answer: the coordinator profile has no term start or end field (availability, capacity, email, focus, hours, on-request, requirements, scheduled hours — and nothing else), which is the concrete reason renewal cannot be reminded, rostered or offboarded automatically. Two dead forms should be archived: webform_1680 "Current Facilitator Reapplication", last used Feb 2019, and webform_8242 "Facilitator Evaluation", last used Aug 2023. (Verified against the database, 2026-08-14) A track opened 08-11 now builds exactly the missing piece — term fields, renewal reminders, onboarding and offboarding — which is why this row reads changing rather than stable. Refined 2026-08-20 by the cycle review, and it makes the missing piece smaller than stated above. The claim that there is nowhere to put a term stands — there is no term field — but the recurrence already carries most of one: field_coordinator_hours is a Smart Date field, all 70 facilitators have a weekly rule (7,744 instance rows, 72 rules, 6 rows with no rule at all), and the rule records a term start for everyone. A term end exists for 13 of 72 — UNTIL=<date> on 12, COUNT=99 on one — while 59 (82%) are unlimited, running forward with no end at all, which is the honest reason renewal cannot be rostered automatically today. So the Phase 0 question is narrower than "add term fields": require an UNTIL when hours are entered and the recurrence becomes authoritative, or add separate fields and accept two sources of truth for data that already drives live scheduling. Do not add fields before that is decided with Kate.#jobs. An intake form has existed for four years with nothing downstream of it. A track opened 08-13 closes the loop — intake → light review → automatic post → auto-expiry — deliberately leaving member-to-member posts alone.planned, not changing — a built path nobody uses is not an automated process. D1 because nothing tells a member that the option exists, what rate applies, or when it is appropriate to claimeffort_hrs_month field this registry is missing — worth wiring the two together rather than estimating twicemakerspace_legal_gate, plus the accept page moved to the scroll-box style): an instruction callout on arrival, a jump link to the boxes, and native validation replaced by an in-page error naming the box still unticked. Watching: whether the 187 accounts still behind the gate now clear it, and whether staff stop hearing about it. It still tells nobody when it fails, which is why A stays 4 rather than 5. Mechanics re-verified 2026-08-24 by the member-onboarding flow check, which now photographs this branch: a fresh account that has never accepted lands on the gate, gets the "Please accept the updated terms to continue" callout on arrival, a working "Go to the boxes" jump, and — pressing Confirm with nothing ticked — the real message "You must accept the Terms and Conditions before continuing" instead of the old silent no-op. That confirms the repair, not the outcome: whether the still-gated accounts actually clear it, and whether staff stop hearing about it, is what this watch is still open on. D scored low deliberately: a README now describes it, but nobody has yet worked from it unaidedscripts/workflow-health.sh on 2026-08-17 to close that: one for a smart group whose cache stops rebuilding, one for a group fed by an automated write path that has gone quiet. The second immediately named a real outstanding case the audit had left behind — the Guest Waiver webform's group-add has been dead for 145 days, harmless only because that audience is now activity-driven. Leaves watch when a freeze is caught by the check rather than by a personscheduled_date NULL and no approval, so the draft is inert (note, corrected 2026-09-05: a job-row count is NOT the safety test — a test send creates a job row and computing recipients fills civicrm_mailing_recipients, which together read like a premature submission. The honest test is scheduled_date and approval_status_id both NULL with no non-test recipient queued); monthly_active is false, so our own scheduler ignores it. Draft-only cron shipped live 2026-09-09, flag still deliberately OFF. The reason it could not be switched on was code, not editorial: cron called the create-and-send path under a different mailing name from the draft staff edit, so flipping it would have mailed the raw template to ~6,000 people and left the edited issue behind. The monthly path now only builds a draft and emails alert_email a link. Proven on test before shipping, against the honest test this row already insists on: with the flag forced on, cron created a mailing with scheduled_date NULL, approval_status_id NULL and zero job rows, and cron correctly skipped while that month's issue already existed. A near-miss worth recording: committed config carried alert_email: '' against live's staff@makehaven.org, so the config import would have blanked the very address this feature emails — caught during reconciliation, and the feature would otherwise have shipped mute. monthly_active is now safe to flip and remains JR's call; flipping before 2026-10-01 points the first run at September's already-sent issue too. It was created this way deliberately: drush mh:digest-create-monthly --force has no draft stage — DigestScheduler calls submitMailing() then processMailingNow() in one execution and backdates the schedule, so the documented "first send" path would have mailed 6,600 people with nothing a human could inspect in between (and it still errors on test, since 2026-08-18). JR's call 2026-09-03: a human approval step until this is dialled in. Two things landed the same day that make an issue writable at all: a Notice content type (/node/add/notice) so staff write member news as site content that feeds both newsletters from one source instead of typing it into Mailchimp, and a newsletter console at /admin/makerspace/newsletter with a preview that renders the real email and can send a test copy — previously the only way to see an issue was to mail it. Both previews render against live data with 0 unresolved tokens. A moves 2 → 3 and D 2 → 3: the issue can now be seen and tested before it goes, which was the missing feedback loop. Watching: the Saturday 2026-09-06 send — whether Kate can edit, preview-as-a-contact and test without help, and what delivery looks like against the weekly baselines. Open: --no-send on both create commands and taking the send out of --force for the monthly, so the accidental mass-send path stops existing rather than being avoided by convention. Preview as a specific contact, never as yourself — rendered_view is interest-filtered, so a staff copy is not representative.max_execution_time killed the send rather than it throwing, so every error-log-based monitor we had was structurally blind to it. Resolved by raising mailerBatchLimit 0 → 100 so the batch fits inside the execution ceiling — a CiviCRM setting, not Drupal config, so it does not travel with drush cim and must be set on any environment that will send at scale. Alerting shipped live 2026-08-20 (makerspace_digest_scheduler 7ad888c): two checks on every cron tick — a running job whose delivered count stops moving, and a schedule whose window passed with no mailing created at all, the failure that leaves no row and no job to find. Alerts reach Slack, staff@makehaven.org and watchdog. The same release fixed an ordering bug that had recorded no run-state for W31–W34, so status had been reading a month stale. A moves 4 → 5: failure now reaches a human who was not looking for it. Watching: the 2026-08-26 send, and whether the missed-send check stays quiet on live rather than crying wolf Capped-email release SHIPPED LIVE 2026-08-28. Eight full cards, then up to 12 sparse lines in a new "Also Happening" block, then a full-calendar button; interest matching moved to child terms only; and the subject line is now drawn from the last_chance block every recipient receives identically, so it can no longer name an event absent from a reader's copy. The block needs a manual CiviCRM edit that no config import performs — {events_email_listing.also_happening} in message template 176 — rehearsed on test after the live-DB clone and then applied on live; verified rendering 7,039 bytes / 10 links for a real group-64 recipient on both. Now also watching the Wed 2026-09-02 send: total clicks against the 57–71 baseline, since capping the long tail is the risky half. Announced as changelog #44797. 2026-09-03 — the digest had been advertising the urinal deodorizer. Kate raised it after the 09-02 send, seconded by Lior and Ashley. The "New activity" block's bundle filter on views.view.digest_weekly_content included material, the store inventory type, and measured on live 11 of the last 12 eligible nodes were store restocks — so that block was a restock feed in practice, not an editorial one. material removed from that display only; featured keeps it, since it requires promote = 1 and a staff member can still feature one deliberately. Shipped live the same day with the notices block ({events_email_listing.notices_recent}) inserted above it, so staff-written news now occupies the slot restocks were filling. Announced as changelog #44943. Watching: the 2026-09-09 send, for whether that block now carries anything a member would care about — the risk is not noise any more but emptiness, since nothing else was being created.field_reservation_cancellation, a vestigial field from the older reservation content type that none of the 914 cancelled appointments on live carry; all 914 stay published and keep their slot blocked. The booking form's JavaScript builds its list of taken slots straight from that view, so the lost capacity is invisible rather than merely cosmetic. Volume is steady rather than dramatic — about 31 cancellations per 30 days — which is why it went unnoticed for so long. Fix staged 2026-08-20 (config only: the view now filters field_appointment_status != canceled), deploy pending; verified against the real view query, which excludes the cancelled node and still blocks scheduled ones. Noted while fixing and not shipped: the view still pages at 15 rows sorted newest-first, so a facilitator with more than 15 appointments may not have every reserved slot rendered. Second, separate fault found 2026-08-21 from Lior's water-jet report: the stored time can be hours off the real session. The time is computed as posted-shift-start + slot offsets, and the booking link's start_time was carrying the shift's end — every one of one badger's bookings sat exactly one shift-length late (a checkout displayed 7:30pm for a 3–6pm shift), while a bare H:MM with no meridiem parsed as the small hours for five others. 47 appointments across 2026 affected. Fix staged 2026-08-21: the slot base is now reconciled against the shift the host actually posted, plus drush appointment-facilitator:retime-appointments --apply to repair the stored ones and audits to keep it visible. Arrival statuses were classified against the wrong times and need recomputing after the repair Both faults SHIPPED LIVE 2026-08-21 (release 2026-08-21-member-success-storage-appointments-batch). The cancellation filter now reads field_appointment_status != canceled, and the retime repair ran on test and live: retime-appointments --apply corrected 47 appointments across 2026 (identical count on both environments), arrivals were recomputed, and audit-times --since=2026-01-01 now reports every appointment inside its facilitator's posted hours. Watching for: a scheduled appointment must still block its slot, and the 15-row pager limit noted above is unfixed. Cosmetic bug found while running it — backfill-arrivals logs "Updated: @updated. Skipped: @skipped" with unreplaced placeholders, so the real counts are lost. Separate gap found 2026-08-24 by the badge-earning flow walk, one layer up from those repairs: nobody is reminded that a booked checkout is happening. appointment_notifications.settings.reminder_enabled is false on live and all four reminder templates — member and host, subject and body — are empty strings, though reminder_days_before is already set to 1. Both sides are emailed when the appointment is made and then hear nothing until it is time. The mechanics of booking have now been fixed twice; what has never been built is the thing that gets both people to turn up, which matters because the characteristic failure of this process is a no-show and a no-show is what leaves a badge stuck at pending. Needs copy for four fields plus the switch — no code. Done 2026-08-25 (config, staged not deployed): reminder_enabled on, and member and host reminder templates written — the member gets when, who, which tools and a cancel link that now genuinely releases the slot; the facilitator gets who is coming plus a prompt that the badge is still not granted until they approve it. Calendar invites needed nothing: calendar_invites_enabled is already true and invites attach at booking (member_scheduled / host_scheduled / canceled), which is the right moment — a reminder does not re-attach one. Both emails were rendered and sent through the real handler locally with every token resolving; the cron selection path is not yet exercised (the local Docker daemon dropped first) and is listed as a test-env check in DEPLOY_TODO rather than assumed.field_civi_event_badges) includes the badging session and the instructor is the badger, so the instructor's sign-off should be the checkout. In practice nothing happened after class: participants stayed Registered (no Attended on any 2026 lathe class), the instructor "class checkout" page had been used once, ever, and even when used it only stamped a date — the badge still needed a separate facilitator checkout. So students did what the badge page told them: 6 of 8 Metal Lathe and 3 of 5 Bridgeport training-documentation submissions since May came from class registrants, each landing in JR's inbox as "Documentation review". Three 2026 lathe classes (~14 students) produced 1 active badge. Staged 2026-09-04, not deployed (release 2026-09-04-class-checkout-issues-badge): the instructor's click now marks Attended and activates the badge (or activates it on the quiz pass), a rare "attended, did not pass → retake" action exists, and class registrants are no longer asked for documentation. Also found: the badge page's class-registration bypass and its "Upcoming classes" list had never worked (entity query on a CiviCRM-entity Drupal field returns nothing). Watching for after ship: Brandon and Ken actually using the page at the end of class (post-event reminder already links it), documentation-queue volume, and Attended counts on badge classes. SHIPPED LIVE 2026-09-09. Watching exactly what the row already named: whether Brandon and Ken use the page at the end of class, whether the documentation-review queue stops filling with class registrants, and whether Attended counts move on badge classes. A2 because the page existed but did nothing useful; D1 because no doc said the class is the checkout until the module README §4 written todayinstructor_companion — is a hard fatal and has never worked: AttendanceForm::buildForm() calls $this->entityTypeManager(), which FormBase does not provide, so every instructor who clicks it gets a white screen. Six real attempts in the retained log window. It is not wholly unmarked, because staff can set participant status by hand in CiviCRM: over the last 30 days 12 marked Attended and 2 No-show against 110 still sitting at Registered, so the fallback runs but rarely. Fix staged 2026-08-17 (instructor_companion 19bae63), deploy pending — the form now injects the entity type manager it was calling as a helper, verified locally by building and submitting against a real past class. The fix reached live on 2026-08-20 and the fatal is gone — live composer.lock pins 19bae637, and the last AttendanceForm::entityTypeManager() error in live watchdog is 2026-08-18, before the deploy. The outcome has not moved: over the 30 days to 2026-08-20, 119 Registered against 12 Attended and 2 No-show — the same ratio as before the fix. So the dead tool was never the whole problem; nothing prompts an instructor to open the screen, and the post-class reminder does not ask for attendance. Watching whether the unmarked count falls now that the form works; if it has not by mid-September, the next move is a prompt, not a repair. Scored A2 rather than A3 because the tooling half is dead and the surviving path is a human editing CiviCRM records, and D1 because nothing describes that fallbackSelfSvcUpdate form accepts only a valid checksum or the edit all events permission — being logged in as the participant does not count. The cancel link is generated inside the registration confirmation email, and checksum_timeout has no row in civicrm_setting, so we run CiviCRM's 7-day default. Measured over 180 days on live: 579 of 960 registrations (60%) are made more than 7 days before the event, mean gap 17 days, max 281 — so for most people the link is already dead when they reach for it, and whether it works correlates with how early they booked. The sharp edge is that the lifetime is baked into the checksum at generation, so raising the setting fixes only newly sent links; every confirmation already in an inbox keeps 7 days forever and staff must cancel those by hand. Fix (raise to 60 days) staged 2026-08-24 — it is a CiviCRM setting, so it does not travel with drush cim and must be set per environment, the same trap that hid the W34 digest stall. FIXED ON TEST AND LIVE 2026-08-24 — checksum_timeout raised 7 → 60 days. Live went from days=7 life=168 to days=60 life=1440, the DB row reads i:60;, and an end-to-end simulation on live (a link minted under the new policy and back-dated 10 days) validates — the exact case that had been failing. watch, not stable, for two reasons: the fix is forward-only, so every confirmation email sent before 2026-08-24 keeps its 7-day life and staff must still cancel those by hand until those events pass (JR decided against re-sending confirmations); and the value lives in civicrm_setting, which terminus env:clone-content overwrites — live now holds the right value so a clone propagates it, but any future environment rebuild should be re-checked with scripts/civicrm/set-checksum-timeout.php. Moves to stable once staff confirm the reports have stopped. Public ledger #44560, flipped to shipped. D scored 1, not ?: nothing described this behaviour and staff had no way to tell a real permissions fault from an expired linkwebform_497's acknowledgement handler addressed […values:e_mail_address:raw] while the element is e_mail_address_25, so it resolved empty and Webform declined to send — for every submission the form has ever taken. The token is now correct on live and both handlers fire. A re-scored 1 → 4 in one step: the old 1 scored the outcome (no acknowledgement ever arrived), not the mechanism, which was always an automatic Webform email with one wrong character. Two things the fix does not cover — the 129 people who submitted and heard nothing have still not been told, and the form requires topic images, machines and consumables, so a guest speaker offering a talk cannot submit it honestly/admin/education console — the half that survived the rollback/register/instructor, has dead-ended on "Page not found" since 2026-08-14. The account is created and the applicant is stranded on the next click — one confirmed casualty in the retained log window (uid 5733, the applicant the education manager was onboarding when they reported it). Two causes, either sufficient: the master agreement's alias moved to /instructor/agreement on 08-14, leaving the old /form/webform-5220 alive only as a redirect entity; and the redirect module refuses to act on any request carrying ?destination= (RedirectChecker::canRedirect()), which is exactly what the signup hand-off adds — so the redirect never fired. The deeper fault is that this was the one entry point the 08-13 rollback missed: it kept offering a legal agreement before any staff conversation, the specific thing the rollback removed everywhere else. It went unnoticed for three weeks because the path it offered it on was dead. Fix staged 2026-09-08, not deployed (profile_registration 523b359, instructor_companion 8d9ffaa): signups land on /become-instructor, which routes by role, and the post-signup copy no longer promises an agreement. The bigger finding is that the education manager was using that bookmark for a job the site never had a tool for — bringing in a person they had already met and decided will teach. Every door was built for a stranger arriving cold. Built the same day, staged in the same release: a staff-sent invite (/admin/education/invite) — one email, a 14-day link that signs the person in and opens the agreement, signing grants the role and opens the dashboard, outstanding invites listed with Resend. The old bookmark now goes to /become-instructor. Flow check the same evening (test env, 20/21 — the 1 is a stale script expectation): every self-serve step renders and a member proposal really saves, so the machinery is fine. The outcome is not: 0 member proposals in the 5 weeks since propose-first went live, 9 interest/idea submissions in 90 days and not one reviewed (oldest 06-18; the weekly nudge to education@ has changed nothing), the approval email has never been sent on live, 0 signings since May. The self-serve funnel is a working pipe nobody stands at the end of. Watching after ship: the first real invite end to end (email arrives, link works days later, the signed-agreement staff email reads correctly), whether Ashley's team reaches for the invite rather than the old bookmark, and whether the two queues get worked once the console is part of a routine SHIPPED LIVE 2026-09-09. /register/instructor now 301s to /become-instructor on live and the staff invite is live at /admin/education/invite. A second, hidden dead end was found on test and fixed in the same release, and it would have made the invite useless: login_destination's redirect_all_to_resources rule fires on every login with no page or role restriction, so it overrode the invite controller's redirect and landed the invitee on the Resources hub reading "Read the agreement below and sign at the bottom" on a page with no agreement. Pre-existing config, which is exactly why local runs never saw it and a live-cloned test did; the invite path is now exempted from the rule. The full walkthrough passes 28/28 on test — invite, email, signed link, agreement, signing, dashboard, role granted. Watching for: the first real invite Ashley sends end to end, and whether the two review queues get worked now that the console is one screen. The funnel's outcome problem is untouched by this release — 0 member proposals in 5 weeks, 9 unreviewed interest submissions, 0 signings since May — so a working pipe still has nobody standing at the end of itmakerspace_entrepreneur_dashboard. Member half was invisible (soft-launch block placed in the admin theme); fixed live 08-09badge_gate_mode rather than reverting Badge gate SHIPPED LIVE 2026-08-21 with badge_gate_mode: block. The same deploy's makerspace_reservations_update_9011 repaired 117 of 117 placeholder reservation titles (the %AutoEntityLabel% rows staff were seeing), leaving zero on live. Watching for legitimate bookings being refused — soften via badge_gate_mode, do not revert./tasks; extending it to members is gated on first building a way to certify who is competent to repair what. Two changes shipped live 2026-09-09. A repeating series decided whether to generate its next occurrence from field_task_status rather than from the task_completed flag that actually says the job is done — so a series left open/in_progress produced duplicates and one left incomplete after completion stalled forever. Both directions now follow completion, with kernel tests either way. Members also gained /tasks/request, a members-only form for proposing a maintenance job on a tool or area — the first member-facing entry into this process. Watching for: exactly one open occurrence per recurring series (the bug cut both ways, so look for duplicates and stalls), and whether member-proposed tasks actually get triaged rather than becoming a new silent queue. The spreadsheet → /tasks migration is still the open intent; this release improved the machinery, not the migrationasset_log_entry holds 117 records and members report faults that feed the KPIs, with a tool-quality report checking configuration. The earlier "did not get traction" note was historical. New concern from the data: logging has fallen off a cliff — roughly 29/month across April–June, then 5 in July and 3 so far in August. Either faults dropped or reporting did, and those need different responsesSyncManager sends to Xero as the bill line description — was being blanked by every background save, because the label pattern rendered access-controlled fields as the anonymous account cron runs as. 290 of 307 requests had lost theirs, and those bills are already in Xero carrying the damage. Both shipped live 2026-08-15 (xero_bills_sync ac54dcb): the member list now carries For / Hours / Amount, the label pattern moved to property tokens so background saves stop rewriting titles, and update_9005 rebuilt all 311 titles on test and live. Verified as a real member, not as an admin — field permissions make those two views differ. Watching: that titles survive the next 1st/15th consolidation run, since cron is what erased them before. Staged 2026-08-18 (a1748d7): the free-text "Pay to (Xero contact)" box no longer sits in the middle of every request form for staff — it moves into the collapsed Advanced group and only comes back out when the payee has a business contact recorded. That review found the LLC billing path had been entirely dormant since it shipped — no account had a business Xero contact set — and JR recorded the first one the same day (Aaron Monikowski → Glow Worm Ideas, live). Two things surfaced from switching it on: his form save silently failed to land (written via drush instead; cause unidentified, worth chasing if it recurs), and the choice label rendered the raw Xero ContactID, which a second fix resolves to the business name. Scope, recorded 2026-08-28 from the Q1 build: xero_bills_sync shipped 2026-01-08 carrying four distinct real-world payment paths — a contractor invoicing hours, a member or volunteer out of pocket for materials, an instructor or contractor filing a timesheet, and a mileage claim. This row is the first two; timesheets sit under Instructor stipend payment and mileage now has its own row. The module was the most-developed piece of infrastructure of Q1 2026 — 22 reference bumps between 2026-01-09 and 2026-03-20 — and the board report described it as the quarter's finance automation launchconnect.makehaven.org links across 10 nodes and 1 message template repaired, re-run reports 0, and /donate markup now contains none. Watching for a human confirming the Opportunity Fund card is clickable end-to-end; the Pantheon domain removal and the Cloudflare connect A record are still open. Prior state below. 2026-08-24 — one of the two donation routes on /donate has been dead, and nobody caught it. The Opportunity Fund card ("help make membership accessible to everyone") had its icon, heading and description all linking to connect.makehaven.org — the standalone-CiviCRM domain retired when Civi was merged into the main site. That host does not merely redirect badly: its HTTPS does not resolve at all (no certificate; it is the standing action_required in terminus domain:list) and plain HTTP returns Pantheon's 404 Unknown site. So anyone clicking that tile got a connection error, and it produced no server-side error on our side to find — the failure happened entirely in the donor's browser, which is why months of donate-page feedback circled the page without anyone naming this. Node 328 carried three more of the same links; 14 dead links in total across 10 nodes and one CiviCRM message template. Paths map 1:1 (www.makehaven.org/civicrm/contribute/transact?reset=1&id=2 returns 200), so the repair is a host swap: config fix for the staff CRM-contact view plus an idempotent content port, staged 2026-08-24, deploy pending — it writes content, so it must run on test and live. Moves back to stable once the card is verified clickable on live. Found while retiring the dead domain, not by anyone testing the donate page. The acknowledgement half is unaffected and still works: The acknowledgement procedure is followed. Some manual steps are deliberate — handwritten notes on cards — and JR sees room to automate acknowledgements for smaller donations while keeping the personal touch for larger ones.ee72e74 aligns calendar-day overdue and 12 completed months, withholds unsupported retention/LTV claims; Storage e9e2d16 separates unassigned list value and surfaces record exceptions before a filtered roster; Member Success 671d04a fixes contact timing, range/export agreement and snapshot/financial labels. Local 53 unit tests, 3 MariaDB kernel tests and 2 browser journeys pass. Assignment conflicts and missing historical evidence remain; no billing/delivery changes or human re-verification. See website release 2026-09-09-operational-dashboard-corrections.md. Snapshot repair staged 2026-09-09, not Live: September capture stopped on case-equivalent plan keys; headers concealed absent facts. Snapshot 92be39e adds atomic writes, fact-level health, correct storage field mapping and bounded historical recovery; Member Success 7caf648 clears obsolete latest flags. Forty kernel tests pass. Local recovery restored only original-count KPIs and dated donation facts; remaining history stays missing until archived evidence is available. Production recovery/deployment are pending; see website release 2026-09-09-snapshot-integrity-repair.md. 2026-09-10 bundle shipped live — changing → watch. Three faults fixed at once. (1) The monthly snapshot had failed on Sept 1 on a duplicate plan key, so KPI history stopped at Aug 1 and storage facts at July — while the health command reported OK, because headers counted as coverage and empty KPI sets bypassed the check. Capture is now all-or-nothing with rollback, retries the same day, and reports missing data honestly. (2) Retention and revenue were being calculated wrongly: retention now follows each member's own anniversary with leap-day clamping and includes disabled accounts (Aug 2025 reconciles at 23/37), and revenue now states plainly that it measures the current Chargebee cohort's monthly contribution, not historical acquisition. (3) Every KPI now shows when it was calculated and flags figures needing checking. The September facts were recovered on live the same day — KPI facts 0→2, donation 0→1, original org row preserved unchanged at 777 active / 845 total, re-preview empty. Operational note that bit us and will bite again: drush msd:kpi-warm must run after any deploy touching this. A cache clear alone does not refresh persisted payloads, so without it 46 KPIs render "Awaiting refresh to verify the value source" and goal comparisons stay hidden — it looks exactly like the dashboard has stopped calculating. Found because JR asked why /kpi showed calculation timestamps. Watching: whether the Oct 1 monthly capture completes unaided (the first real test of the retry), and whether health goes quiet or keeps warning on the plan/revenue/storage/certification/access history that was NOT reconstructed — that remains separate data work.borrowed_or_missing_without_borrower integrity check conflates "borrowed without a borrower" (a real fault) with "missing without a borrower" (usually expected), and reads as failing because of a stale write-off backlog nobody has triaged — so the one instrument pointed at this process cannot currently tell a fault from housekeepingrk_live_ restricted key in use lacks charge_write, so the site logged an attempt and no money moved. Resolved by 2026-08-18 and verified against Stripe 2026-08-20: the key was granted charge_write, and all three refunds then went through the site's own form — transaction 4851 ($30.00) and 4857 ($5.00) at 23:07/23:08 UTC on 08-18, plus 4866 ($5.00) at 23:14, each matched by a succeeded refund object in Stripe. Nobody is out of pocket, and there has been no refund failure since. The reason this row is watch and not stable is the pattern rather than the incident: this was the second scope missing from that key discovered by a production failure — the store tab pilot hit the same key missing Customers:Write — and both times a member hit the hole before we did. The key's scopes still want auditing against every Stripe write path in one pass rather than being widened one outage at a time; until that happens, assume a third scope is missing somewhere nobody has exercised yet. Found by the 2026-08-17 weekly pulse. (A 2026-08-20 re-check first recorded this as still broken by searching watchdog for errors and finding no newer ones; searching for successes instead showed the refunds had gone through three days earlier. Absence of new failures is not evidence of a fix.) 2026-09-10: counting corrected. Loans due today were being reported as overdue — the manager showed 3 against analytics' 1. Overdue is now a calendar-day comparison with due-today shown separately, and page and CSV date ranges agree. A reporting fix only; the charging path is untouched.makerspace_material_store 6368013), none shipped yet. (1) Repeat adds. Five members in August each created three to seven identical tab lines within two minutes — one added the same $5 access card seven times — and every extra was reversed by hand; they are most of the $257 “removed” that month. The cause was the confirmation, not the members: the modal closed on success and redirected to /store, a different page from the one they were reading, where an unrequested status message was the only evidence anything had happened. Nothing answered “did that work?”, so they clicked again. Now answered before (what is already on the tab), during (progress message, repeat clicks swallowed) and after (the modal is replaced in place with a receipt carrying the new tab balance and a direct undo), plus a confirmation tick on an exact same-material-same-quantity repeat inside 10 minutes. (2) The opt-in card check failed open on every error, so a member whose stored Stripe customer no longer exists could opt in and accumulate an uncollectable tab (uid 3230, 08-24, six failed charges over two days). Permanent rejections now refuse; transient ones still pass. (3) The Stripe idempotency key did not cover the line set, so a second manual charge in the same month — what staff do after removing duplicate lines — was rejected outright by Stripe (uid 3891, 08-23, twice). The old Customers:Write note is superseded: opt-in linkage is no longer the live problem, though it belongs in the one-pass key-scope audit the Overdue late fee row asks for. Two members still need a human, not a deploy: Ellen Carson has $1.50 on an open Stripe invoice since 08-14 that escalated to staff on 08-16 and has sat untouched since (the retry ladder worked; the human end of the loop did not), and Dani Ramirez has $38.00 pending against a freshly-rewritten customer ID nobody has confirmed carries a card — check before the 08-31 month-end run. SHIPPED LIVE 2026-08-28 (release 2026-08-27-workspace-photos-digest-store-lapsed-audit). Rehearsed on test through the real validateForm/submitAddToTab rather than units: the first add created a line, an exact repeat at the same quantity was refused with a confirmation tick and created nothing, a different quantity passed untouched, and the confirmed repeat went through; the receipt rendered with line total, new tab balance and a working undo link. Moves changing → watch. Watching: whether August's repeat-add pattern (five members, three-to-seven identical lines each, most of the $257 reversed that month) actually stops, and whether the 10-minute duplicate_window_minutes default proves right — it is unset, so it lives in code until someone sets it at /admin/config/makerspace/store. Announced as changelog #44798. Not fixed by this and still open: Ellen Carson (uid 1381) $1.50 stranded on an open Stripe invoice, which needs a human.supplier= param. Deliberately not yet reported to the member who found it: verified anonymously only, and ledger items 44325 / 44550 / 44551 stay open until a logged-in look at /store confirms it. Prior state below. Member plywood complaint 08-17 confirmed /store rendered only classic-PayPal buttons (now behind a PayPal auth wall; sales 168/mo Apr → 28 by mid-Aug) with zero links to the tab/Stripe checkout. Fix shipped live 2026-08-20 (config-only): every buy button on /store, material pages and related-supplies blocks now routes to /store/checkout-item/{nid} with a guest-PayPal fallback. Verified on live 2026-08-20 — the staged config is deployed (the local-vs-live config diff no longer contains it). One day of data, too early to call: in-product transactions ran 43 in July and 57 so far in August, and the day after the deploy carried 6 against a recent daily median of 2. Re-measure the PayPal-vs-Stripe split and tab uptake in the week to 2026-08-27 before moving this to stable. 2026-08-24 weekly pulse — the fix itself regressed, and a member found it before we did. The deployed config builds the guest link as href="…webscr?{{ nothing_2 }}", and nothing_2 composes its query string with item_name={{ title }} — but title on this view is the linked title field, so its rendered value is a whole <a href="…">Item Name</a>. The inner quote closes the href early: the guest link points at a truncated, unusable PayPal URL, and the remainder of the query string renders as visible raw text under every Purchase button on /store, on material pages, and in Related Provided Supplies (four display sites). The member-facing Purchase button to /store/checkout-item/{nid} is unaffected and still works, so this is broken for the guest path and ugly for everyone, not a total outage. It never showed before because nothing_2 had only ever been consumed through Views' path: setting, which URL-encodes it. Reported as feedback sid 20328 on 2026-08-21, the day of the deploy; root-caused in the 2026-08-24 pulse; entry and three fix options in DEPLOY_TODO, public ledger #44550. The measurement above is now confounded — any PayPal-vs-Stripe split read in the week to 08-27 is a read of a broken guest link, so re-measure only after the fix. Two related store items came in from the same reporter the same day: material pages reached from /store carry no Request Restock button (ledger #44551), and restock-request emails intermittently omit the supplier and material link buttons — a re-report of the same complaint from 2026-05-11 that was never fixed (ledger #44552).required on profile.main, so 115 of 858 active members (13.4%) could not save any profile edit — not a bio, not a headshot — until they supplied an emergency contact nobody had ever asked them for. That is what dead-ended every "you can fill this in later" path, and it was routinely misreported as "the bio field is required" (bio is not required). Fixed live 2026-08-18: the requirement now applies on the join path only, so new members are still asked while established members get a prompt and a one-purpose form at /membership/emergency-contact instead of a wall. Watching the number: 115 at deploy — if it does not fall, the passive banner is not enough and the answer is an email, not another form tweak. That is the same lesson the discovery fix taught: a deferred field needs an active capture moment or it is not collectedstable; a 2026-08-25 measurement showed it was not. Staff said at the 08-25 meeting that they submit things and never hear what happened. Of 324 submissions ever made, 139 (43%) had no entry on the board at all, the four honest-closure statuses had been used zero times in 174 items, and 82 open items had gone untouched for over three weeks. The mechanism was never broken — items that reached the board did get flipped on deploy — the failure was coverage and follow-through. Backfill took coverage 57% → 99% (302 items); 55 of the missing 139 turned out to be already fixed and nobody had said so. First full pulse under the new loop (2026-08-31) came back clean: every submission reached the board (2 unledgered, both known junk), the three oldest stalled items were given real answers rather than another status bump, and the three people who reported them were emailed automatically. One clean cycle is not yet a closed watch window — hold watch until a second. SHIPPED LIVE 2026-08-26: submitters now get a receipt and a status-change email, the form gained a staff alert (it previously had zero handlers, so submitting notified nobody), the board sorts by reported date, and weekly-triage gained a look-back step because step 1 only ever looked forward from the watermark — which is exactly how the gap opened. Two defects were caught on test before live: publish: false never worked at all (setPublished() takes no argument in D9+, so it silently published — all 303 ledger items and 124 changelog entries on live are published, nothing was ever held back), and the staff email re-sent on every manifest re-run. Watching for: whether the aging count actually falls, and whether the 66 not_actioned items get decided rather than becoming a new silent bucket. Cycle plan routing all 180 open items into 12 work packages at docs/ops/FEEDBACK_CYCLE_PLAN_2026-08-26.md. Originally: a well-documented A3 — worth copying as a patternchanging → watch. Outreach reporting dates aligned, first-contact-to-first-positive timing now counts all contacts rather than ignoring earlier unsuccessful ones, and obsolete latest-flags are cleared after the daily snapshot completes. Watching whether the daily run stays clean and whether the corrected timing changes what staff conclude about which interventions work.payment_updated row) but never losses: a member whose subscription cancels mid-recovery drops out of the snapshots with no row, so Confirmed Cancel read 0 for everyone since 2026-04-20 (28 real departures unrecorded; 17 had Christina's outreach within 30 days). Fix staged (makerspace_member_success 79c45cf): departures now auto-log a back-attributed confirmed_cancel, an ms:backfill-cancellations command repairs the gap, the resolution-rate card gained a names drill-down, and the Full Contact Log export now actually exports the log. Move to watch after deploy + backfill; verify Kate/Christina accept the corrected numbers SHIPPED LIVE 2026-08-21 and the backfill has run. ms:backfill-cancellations processed 27 departures on live (8 skipped as still-active members); the same command had been rehearsed on test with the same 27. Departures now auto-log a back-attributed confirmed_cancel, so Confirmed Cancel should stop reading 0. Still open and the reason this is watch not stable: Kate and Christina have not yet been told what the corrected numbers mean — resolution credit is back-attributed within 30 days, and "self-recovery" rows are members who paid with no human contact. Their reports were ledger #44334.makerspace_email_guard). Scope had been recorded as unmeasured; it was not small. Across the CRM's 1,393 distinct email domains, 114 resolve to nothing at all (no MX, no A, no AAAA), covering 173 contacts, 62 with no Drupal account — people who got a contact record, have heard nothing from us since, and read as no-shows rather than broken signups — plus 120 Drupal accounts on a dead domain. Dead domains are now blocked at the register form, every webform, and CiviCRM's own event registration, which is where most of these actually came from and which no Drupal form alter can see. Live typosquats (gamil.com, oulook.com) are only suggested, never blocked, because gmx.com, aim.com and qq.com are real domains members use and sit just as close to a big provider. Fails open if DNS is unreachable, so an outage cannot stop signups. Watching two things: that nobody reports a legitimate address being refused (the tell is a "skipped the deliverability check" warning in watchdog — none at deploy), and that the weekly cron re-scan keeps the report honest. A4 not A5: it runs unattended and logs, but nothing tells a human unprompted. The backward-looking cleanup of the 62 is a separate open task and needs a person, not code. Watch check 2026-08-20 (two days in, holding but too early to close): across the 12 days watchdog retains, the module has logged exactly one line — the weekly re-scan notice on 2026-08-18 — and zero "skipped the deliverability check" warnings, which is the stated tell for over-blocking. The re-scan has only fired once, so its cadence is asserted rather than demonstrated; re-check after the next two Mondays before moving this to stablefield_user_chargebee_id and drops the plan write when no account carries that ID yet — no queue, no retry. In one 12-day log window 14 of 16 "No user found for Chargebee ID" warnings resolved to accounts that exist now, most created the same day. The miss is permanent because only subscription_created/_updated/_reactivated write the plan; renewals and payments do not, so a member who simply renews is never repaired. It surfaced as members reading "Unassigned" on the membership snapshot: 9 active members, 8 of them genuinely paying, about $340/month absent from MRR because the finance queries treat "no plan row" as "not a Chargebee member". The 8 were repaired by hand on live 2026-08-20 (2 were not linked to Chargebee at all and had their customer ID set; one owned two customer records and was linked to the live one), taking Unassigned 9 → 2. Stays broken because the mechanism is unchanged on live: the fix — a daily PlanReconciler cron sweep that re-derives from Chargebee, plus chargebee:reconcile-plans — is staged, deploy pending (chargebee_fetch_data 528e9a5, confirmed still on neither test nor live as of 2026-08-20). Move to watch after it deploys and confirm the sweep reports 0 repaired. The two members still Unassigned have no Chargebee record at all and need a person, not code Reconciler SHIPPED LIVE 2026-08-21 (chargebee_fetch_data 528e9a5). The daily sweep now runs in cron; the first live run reports 3 checked, 0 repaired, which is the expected result — the backlog had been cleared by hand on 08-20. The mechanism is no longer the unchanged one described above, so this moves off broken. Watching the first live cron day for Chargebee plan sweep: in watchdog. The remaining two members (jwthomp14 uid 5490, mjosepk12 uid 5527) have no Chargebee record at all and still need a person, not code./workspace (floorplan v2 overlays the drawn plan rather than the schematic), and recent applications on /admin/workspace. Still watching the first real post-deploy application for both the applicant receipt and the staff email, and Kate has not yet been told her manual acknowledgement step can stop. Photos on the listing have never worked — found 2026-08-27. The card template and ListingBuilder have rendered a workspace photo since the funnel was built, but field_wr_photo was created by an update hook and never exported to the site repo's config — and cim deletes active config missing from sync, so every deploy purged the field, its instance, both display components and the workspace__field_wr_photo table. Live carries schema 9005, proving the hook ran, with no photo field present and all 15 cards falling through to “Photo coming soon”. Fix staged 08-27 (7eb8e65 plus the four config items) so config import creates the field instead of purging it; verified locally by deleting the field, running cim, and round-tripping a real upload through the edit form and onto a card. Marked broken rather than watch because the defect is still on live — it returns to watch when the next bundle ships, and the photos then need a human to actually upload them. Photo fix SHIPPED LIVE 2026-08-28 (release 2026-08-27-workspace-photos-digest-store-lapsed-audit). Verified on live: field_wr_photo, the workspace__field_wr_photo table, the image_image widget and the image@large formatter all present after cim — the deploy that used to purge them now creates them. Returns to watch as this row said it would. What is still not delivered is the photos themselves: all 15 cards still read "Photo coming soon" until staff upload, so no changelog entry was written and none should be until they do. Ledger item 44238 (reported 3× by Kate, 11/18/24 Aug) flipped to shipped and both reporters were emailed. Two more defects on the public page, found by JR 2026-08-28 — the page has been showing prospects contradictory information. The interactive floorplan's base drawing had monthly prices baked into it, from an older plan whose 1st-floor rooms are drawn 174/179/176 sq ft against the entities' real 152/148/151 — so the drawing contradicted the live price chips drawn on top of it, the cards beside it, and a second static copy of the current price-free plan that the node body rendered directly above it. The listing also rendered twice, once from the body's [[WORKSPACE_AVAILABILITY]] token and again from a leftover block placement at the foot of the page. Separately, every one of the six "Inquire About This Space" buttons linked to the bare inquiry form: the applicant got no confirmation that the space they clicked had registered, and staff received applications with no way to tell which unit they were about — which is the half that costs real work, since the whole point of publishing availability is to route the enquiry. Bonus, found while in there: <meta name="description">, og:description and twitter:description on /workspace have been publishing the literal string [[WORKSPACE_AVAILABILITY]] to Google and every social card, because the metatag defaults map all three to [node:summary] and the body had no summary. All four staged 2026-08-28 (workspace_rental c9ebccd, theme ec7a7b3, webform_2093 config, block deletion, and a content port for the node body). Cards now deep-link ?workspace_id=<id>; the form greets the applicant with the space it resolved and presave stamps the readable label so the staff email reads "Space of Interest: 1st Floor — Room 7 (230 sq ft, door)" instead of "Workspace ID: 19". The base drawing is now the price-free 2023 plan with all 14 regions re-traced (1600x664 → 1200x502) and every sq-ft label matching its entity. Deploy-order caveat worth keeping: the duplicate block was the only thing attaching the listing's CSS/JS to the token-rendered copy — renderInIsolation() discards #attached — so deleting the block without the theme bump renders the floorplan as unstyled black polygons. Marked broken rather than watch because all of it is still live; it returns to watch when the next bundle ships. Not fixed, noted: basement Bay #1 (98 sq ft on the drawing) has no workspace entity, so it is inert on the plan and absent from the cards — staff decision, not a code fix. SHIPPED LIVE 2026-08-28 (release 2026-08-28-workspace-page-and-entity-security). Verified on live: one floorplan where there were two, the drawing carries no prices, 3 available spaces render green with a live price chip, 11 rented are hatched and labelled "Rented", the 2 rooms we do not offer are greyed, no JS errors. Inquiry deep links verified for ids 1/6/19 — each prefills and greets with the right space and specs, an invalid id degrades to no notice. <meta name="description"> and og:description are prose again instead of the raw [[WORKSPACE_AVAILABILITY]] token. Ledger 43642 ("Workspace page does not match the spaces we actually have", reported 2026-07-23 via sid 20033, open as planned for five weeks) flipped to shipped and Kate was emailed; changelog #44806 and #44807 published. Watching: whether inquiries now arrive carrying a space — the whole point is that staff can route them, and nobody has submitted a real one through the new path yet. The live submission was deliberately NOT rehearsed on live (it would email three staff addresses plus an applicant receipt for a fake person); it was proven end-to-end on test through the real submitFormValues, where the staff email read "Space of Interest: 1st Floor — Room 7 (230 sq ft, door)" and a tampered value was overwritten. So the first genuine live inquiry is the thing to check. Also still true: basement Bay #1 (98 sq ft) has no workspace entity — JR accepted it as un-rentable for now, and it renders as "not currently offered" rather than as the brightest room on the plan. Cache defect found and fixed the same day (2026-08-28). The drawing was correct on the server and wrong in the reader's browser — see Deploy pipeline for why. Live now serves floorplan-base-2023-layout.png?v=<content hash>; the old URL 404s.docs/ops/DISPLAYS.md, which is why D moves 1 → 2. And a shared self-heal runtime shipped live 2026-08-20 (makerspace_kiosk, first consumer access_display): the boards now poll with backoff, show a status chip carrying a ticking clock and a liveness dot, and reload themselves when a feed goes stale instead of sitting there looking healthy. That last part is the real fix — the member faces board failed silently, because its fetch errors were swallowed and its grid is append-only, so a dead feed rendered identically to a live one. A moves 2 → 3: recovery is automated, provisioning is still by hand. Watching: whether the chip reads green on the physical screens (still unconfirmed by a person at the hardware), and whether the remaining consumers — facilitator_display, makerspace_screen_slideshow, makehaven_tasks — get moved onto the shared runtime.139 improvement candidates, ordered by impact, automation, draft plan priorities and votes. This is an opportunity inventory, not a committed work queue. Human work can be appropriate and effective. Select work using actual problems, staff effort, expected benefit and available capacity.
139 of 212 processes are here; 73 are not. A row earns a place through a recorded gap or its automation/documentation scores — it is broken or optimizable, or it is not yet both automated and documented and it matters enough to be worth someone's week. A process that is automated, documented and working is finished, not merely un-started, and putting it on this candidate list only makes the list longer. Inclusion does not commit anyone to a project.
The base score is impact × how manual it still is, out of 20. That is arithmetic, and arithmetic does not know which of these actually hurts on a Tuesday. Anyone with a MakeHaven account can push a row up or down, and the net of those votes moves the score by up to ±5 — enough to lift something several places, not enough for a handful of early clicks to bury a safety process nobody happened to vote on.
The plan gets a say too. 80 rows name the strategy acting on them, and 8 of those sit under a P1 commitment — the board's own short list. A P1 lifts a row by 4 and puts it on this tab whatever its scores say, because that is what choosing it meant. P2 adds 1. A registry that ranked purely on its own arithmetic would happily bury something the organisation had just committed to.
So three things decide the order, and all three stay visible on the row:
I4 × A2 is the arithmetic, +4 is the plan, +2 is the
room. If a row moved, it says so. The input still missing is effort in staff hours, and it is
the one that would change this order most.
Priorities come from data/strategies.csv, read at build time rather than copied
into rows. The plan is a live draft heading for a shorter priority list, so
when it is finalised the update is that one file and a rebuild — no row-by-row re-tagging,
and every score on this tab moves with it.
docs/ops/RENEWAL_CALENDAR.md.
Facilities & Equipment
/tasks; extending it to members is gated on first building a way to certify who is competent to repair what. Two changes shipped live 2026-09-09. A repeating series decided whether to generate its next occurrence from field_task_status rather than from the task_completed flag that actually says the job is done — so a series left open/in_progress produced duplicates and one left incomplete after completion stalled forever. Both directions now follow completion, with kernel tests either way. Members also gained /tasks/request, a members-only form for proposing a maintenance job on a tool or area — the first member-facing entry into this process. Watching for: exactly one open occurrence per recurring series (the bug cut both ways, so look for duplicates and stalls), and whether member-proposed tasks actually get triaged rather than becoming a new silent queue. The spreadsheet → /tasks migration is still the open intent; this release improved the machinery, not the migration
Facilities & EquipmentP2Develop
field_civi_event_badges) includes the badging session and the instructor is the badger, so the instructor's sign-off should be the checkout. In practice nothing happened after class: participants stayed Registered (no Attended on any 2026 lathe class), the instructor "class checkout" page had been used once, ever, and even when used it only stamped a date — the badge still needed a separate facilitator checkout. So students did what the badge page told them: 6 of 8 Metal Lathe and 3 of 5 Bridgeport training-documentation submissions since May came from class registrants, each landing in JR's inbox as "Documentation review". Three 2026 lathe classes (~14 students) produced 1 active badge. Staged 2026-09-04, not deployed (release 2026-09-04-class-checkout-issues-badge): the instructor's click now marks Attended and activates the badge (or activates it on the quiz pass), a rare "attended, did not pass → retake" action exists, and class registrants are no longer asked for documentation. Also found: the badge page's class-registration bypass and its "Upcoming classes" list had never worked (entity query on a CiviCRM-entity Drupal field returns nothing). Watching for after ship: Brandon and Ken actually using the page at the end of class (post-event reminder already links it), documentation-queue volume, and Attended counts on badge classes. SHIPPED LIVE 2026-09-09. Watching exactly what the row already named: whether Brandon and Ken use the page at the end of class, whether the documentation-review queue stops filling with class registrants, and whether Attended counts move on badge classes. A2 because the page existed but did nothing useful; D1 because no doc said the class is the checkout until the module README §4 written today
Education & Instruction
civicrm_value_funding_7 holds 46 records; 6 have 2026 due dates and 5 of those 6 carry a submitted link. So roughly five grants were submitted this year, not zero. The win ratio KPI is fine (12 won of 34 decided = 35%, reported 32%), so this is one broken metric, not a broken dashboard. The pipeline has no submitted status at all — it runs researching → waiting → won/lost/abandoned — so "submitted" is inferred from a link field and the period is keyed off the due date. That inference is what fails
Development & FundraisingP1Grow
scripts/daily_log_triage.py runs a pass, and a handful of modules alert into Slack. The signal it all rests on is badly degraded. Over the 12 days watchdog retains, 355,028 of ~410,000 rows are a single warning — Theme hook %hook not found., the known search_api shutdown-phase flood whose 2026-08-03 fix missed site_content and never stopped, at roughly 29,600 a day. Add 32,315 debug-level access_display rows for a service account and the bot-driven 404s below, and real errors are about 0.2% of the log: 533 CiviCRM, 552 UniFi, 130 geocoding, the rest in single digits. Two consequences, one obvious and one not: triage means reading past a wall of noise, and the volume is itself a database write load on a site with standing performance complaints. The pattern this row exists to name: the W34 digest stall left no error at all because PHP killed the send rather than it throwing, so every error-log-based check was structurally blind; the late-fee refunds were first re-recorded as still broken because a search for new failures found none, when a search for successes showed they had gone through. Absence of errors is not evidence of health, and nothing here is built to know the difference. Update 2026-08-24 — the flood stopped on its own and the instrument is usable again. Theme hook %hook not found. fell from ~40,000/day to 3,526 on 2026-08-18 and to zero since; nothing was deployed to fix it, so the credit goes to the 2026-08-18 CiviCRM recurring-event cascade fix rather than to the missed site_content patch. Retention recovered from ~2 days to 14.6 days, and the 315k theme rows still in the table are the pre-08-18 tail aging out. The 2026-08-24 pulse read the clearest severity≤3 trend in months — all known noise (UniFi listUsers 28–48/day, CiviCRM single digits, Calendly notices), with makerspace_dashboard geocoding also stopping after 08-20. This row stays optimizable rather than moving up: the noise was the symptom, and the structural point is unchanged — nothing here tells a human unprompted, and the same pulse still found the /store regression only because a member filed feedback, five days after it shipped. Scored A2 because a human starts every pass, D2 because the pulse and workflow-health docs describe it, I4 because the failures it misses are billing and delivery ones
Platform & MetaDocsWorkflow health · Pulse log
broken was the wrong word; what is left is an end state someone can name and has not been reached, which is what optimizable is for. There is still no periodic sweep — detection depends on someone happening to check, which is how two payments once sat undelivered. The failure is visible in Xero if someone looks, and responsibility sits with the staff member who initiated the payment to their own contractor, per the procurement policy's budget-line ownership.
Finance & AccountingDocsProcurement policy
optimizable — work is landing, not merely wished for. JR: "this is a weak point." Calls go to a Google voicemail box that often sits unanswered for a long time because of staff capacity. The voice AI routing track shipped its Phase 1 foundation live on 08-07, inert behind config; the Voicemail-Tool is written and pre-launch, awaiting a Twilio number
Communications
access_control_api_logger's /api/v0/ routes are all _access: 'TRUE' with no caller auth, exposing the door grant-or-deny decision, member name/UUID/status/photo, and the full badge catalog to anyone on the internet. Re-verified 2026-08-20: an anonymous GET /api/v0/permissions/list still returns HTTP 200 with the whole catalog. The real fix is blocked on hardware — the ESP32/Home-Assistant readers are live consumers and must send the new auth before we enforce it. ~~The unblocking task is small and nobody is on it: find the readers' egress IP~~ DONE 2026-08-24 — the egress IP is 32.218.105.26 (SNET, the building's ISP), identified from three days of live nginx access logs exactly as predicted: the module logs no caller IP, so it had to come from Pantheon's logs. /api/v0/ can now be restricted at the Pantheon edge with no firmware change, which closes the internet exposure that makes this CRITICAL. Caveat to write into the rule: that address is the whole building's NAT — readers, kiosks, staff laptops and guest wifi all egress through it — so the allowlist closes the internet and not the LAN. It is an interim mitigation and does not retire the HMAC/shared-secret work, and it must be re-verified after any ISP or router change, since a dynamic reassignment would break every door in the building. Full finding and remediation tracks in the security ledger
Access & SafetyDocsSecurity audit ledger
composer audit) is the only thing that catches contrib security advisories, and it runs quarterly — so an advisory can sit unpatched for up to a quarter. This deploy found three moderately-critical ones published 2026-09-02 (drupal/ai XSS + access bypass, ai_translate access bypass), all on enabled modules, and caught them at 8 days only because a deploy happened to run composer audit in its prep. Patched same day to ai 1.4.8 / ai_translate 1.4.1. Worth deciding whether dependency-advisory checking should run on its own faster cadence rather than riding the quarterly sweep.
Platform & Meta
webform_10645 "Current Facilitator Application Update" shows two clear campaigns a year: Feb 2025 (40), Jul–Aug 2025 (33), Jan 2026 (31), Jul–Aug 2026 (38) — against 54 facilitators, roughly a 60–75 per cent response each cycle. The six-month term is real and is actually being run. What is missing is not the process but somewhere to put the answer: the coordinator profile has no term start or end field (availability, capacity, email, focus, hours, on-request, requirements, scheduled hours — and nothing else), which is the concrete reason renewal cannot be reminded, rostered or offboarded automatically. Two dead forms should be archived: webform_1680 "Current Facilitator Reapplication", last used Feb 2019, and webform_8242 "Facilitator Evaluation", last used Aug 2023. (Verified against the database, 2026-08-14) A track opened 08-11 now builds exactly the missing piece — term fields, renewal reminders, onboarding and offboarding — which is why this row reads changing rather than stable. Refined 2026-08-20 by the cycle review, and it makes the missing piece smaller than stated above. The claim that there is nowhere to put a term stands — there is no term field — but the recurrence already carries most of one: field_coordinator_hours is a Smart Date field, all 70 facilitators have a weekly rule (7,744 instance rows, 72 rules, 6 rows with no rule at all), and the rule records a term start for everyone. A term end exists for 13 of 72 — UNTIL=<date> on 12, COUNT=99 on one — while 59 (82%) are unlimited, running forward with no end at all, which is the honest reason renewal cannot be rostered automatically today. So the Phase 0 question is narrower than "add term fields": require an UNTIL when hours are entered and the recurrence becomes authoritative, or add separate fields and accept two sources of truth for data that already drives live scheduling. Do not add fields before that is decided with Kate.
Education & InstructionP2GrowDocsCommunity Facilitator Program · Facilitator Training
ee72e74 aligns calendar-day overdue and 12 completed months, withholds unsupported retention/LTV claims; Storage e9e2d16 separates unassigned list value and surfaces record exceptions before a filtered roster; Member Success 671d04a fixes contact timing, range/export agreement and snapshot/financial labels. Local 53 unit tests, 3 MariaDB kernel tests and 2 browser journeys pass. Assignment conflicts and missing historical evidence remain; no billing/delivery changes or human re-verification. See website release 2026-09-09-operational-dashboard-corrections.md. Snapshot repair staged 2026-09-09, not Live: September capture stopped on case-equivalent plan keys; headers concealed absent facts. Snapshot 92be39e adds atomic writes, fact-level health, correct storage field mapping and bounded historical recovery; Member Success 7caf648 clears obsolete latest flags. Forty kernel tests pass. Local recovery restored only original-count KPIs and dated donation facts; remaining history stays missing until archived evidence is available. Production recovery/deployment are pending; see website release 2026-09-09-snapshot-integrity-repair.md. 2026-09-10 bundle shipped live — changing → watch. Three faults fixed at once. (1) The monthly snapshot had failed on Sept 1 on a duplicate plan key, so KPI history stopped at Aug 1 and storage facts at July — while the health command reported OK, because headers counted as coverage and empty KPI sets bypassed the check. Capture is now all-or-nothing with rollback, retries the same day, and reports missing data honestly. (2) Retention and revenue were being calculated wrongly: retention now follows each member's own anniversary with leap-day clamping and includes disabled accounts (Aug 2025 reconciles at 23/37), and revenue now states plainly that it measures the current Chargebee cohort's monthly contribution, not historical acquisition. (3) Every KPI now shows when it was calculated and flags figures needing checking. The September facts were recovered on live the same day — KPI facts 0→2, donation 0→1, original org row preserved unchanged at 777 active / 845 total, re-preview empty. Operational note that bit us and will bite again: drush msd:kpi-warm must run after any deploy touching this. A cache clear alone does not refresh persisted payloads, so without it 46 KPIs render "Awaiting refresh to verify the value source" and goal comparisons stay hidden — it looks exactly like the dashboard has stopped calculating. Found because JR asked why /kpi showed calculation timestamps. Watching: whether the Oct 1 monthly capture completes unaided (the first real test of the retry), and whether health goes quiet or keeps warning on the plan/revenue/storage/certification/access history that was NOT reconstructed — that remains separate data work.
Finance & AccountingP2Develop
undefined: applicants apply and the director decides, with live availability published. Graduation remains undefined — nothing defines when a venture should move on The published-availability half was misinforming applicants until it was fixed live 2026-08-28 — see Workspace rental agreement for the defects and the staged fix; the one that lands here is that enquiries arrived with no indication which unit they were about, so the director had nothing to route on.
EntrepreneurshipP2GrowDocsWorkspaces
field_badge_issuer records that a person is qualified on that tool, and JR's rule is that the naming is permanent — the skill does not leave them, so if they return later as a facilitator or instructor we already know what they can check out. The issuer list is therefore a standing recruiting pool and must never be pruned to match the current facilitator roster. What nothing surfaces is the gap between the two: which tools have members waiting and nobody currently covering them. Measured for the first time 2026-08-25: 9 checkout badges sit on 0–1 currently-active issuer with 159 members waiting between them — Markforged 3D Printer 31 waiting against 5 qualified and 1 active, Tufting Gun 26/5/1, and Blind Stitch Hemmer 24 waiting, 2 qualified, none active. So what staff hit was not an obscure corner; it was the third most under-served tool on the site. Vacuum Forming (7 waiting) is the other with no coverage at all. The intended shape is: read the demand, call whoever is qualified on that tool, and ask them to come back in — most likely as an instructor, if they would run a class, rather than as a facilitator. drush appointment-facilitator:badge-demand --understaffed=1 reports it and prints the call list; it is staged, not yet on live. planned because the report exists and the outreach has not started — nobody runs this on any cadence and no owner is named. Scored A2/I3 deliberately at the low end of the honest range: the drush report is real tooling, but no instance of this process has ever run, and the cost of failure is members waiting indefinitely on tools rather than revenue or safety. Related trap recorded on the same day: do not "fix" an uncovered badge by relaxing the facilitator role filter on views.view.facilitator_schedules — approval separately requires the approve badge requests permission, which most non-role issuers lack, so they would become bookable but unable to grant the badge.
Education & InstructionDocsStaff badge feedback triage · Badge earning flow
instructor_companion — is a hard fatal and has never worked: AttendanceForm::buildForm() calls $this->entityTypeManager(), which FormBase does not provide, so every instructor who clicks it gets a white screen. Six real attempts in the retained log window. It is not wholly unmarked, because staff can set participant status by hand in CiviCRM: over the last 30 days 12 marked Attended and 2 No-show against 110 still sitting at Registered, so the fallback runs but rarely. Fix staged 2026-08-17 (instructor_companion 19bae63), deploy pending — the form now injects the entity type manager it was calling as a helper, verified locally by building and submitting against a real past class. The fix reached live on 2026-08-20 and the fatal is gone — live composer.lock pins 19bae637, and the last AttendanceForm::entityTypeManager() error in live watchdog is 2026-08-18, before the deploy. The outcome has not moved: over the 30 days to 2026-08-20, 119 Registered against 12 Attended and 2 No-show — the same ratio as before the fix. So the dead tool was never the whole problem; nothing prompts an instructor to open the screen, and the post-class reminder does not ask for attendance. Watching whether the unmarked count falls now that the form works; if it has not by mid-September, the next move is a prompt, not a repair. Scored A2 rather than A3 because the tooling half is dead and the surviving path is a human editing CiviCRM records, and D1 because nothing describes that fallback
Education & Instruction
workflow-health.sh — but nothing filters anything: there is no edge rule, WAF or rate limit, so every scanner request is served by PHP. The measurable cost is not just bandwidth. Bots hitting registration URLs for events that do not exist produced 444 CiviCRM fatal errors in 12 days, one URL (/civicrm/event/register?id=765) accounting for 358 of them and still firing; each is a full CiviCRM bootstrap that ends in a crash. The referrers are spoofed (bing.org, google.org, baidu.org — note .org), so this is not a broken internal link to chase. The 404 log agrees: the top paths are /.env, /api/.env, /graphql and a WordPress plugin exploit probe, at 100–230 hits each, alongside 52,930 access-denied and 36,087 not-found rows in the same window. Major correction 2026-08-24 — the overage was measured for the first time, and it is two problems, not one. Three days of live nginx logs (97,703 requests) show 54% of all traffic to the site comes from one IP: MakeHaven's own building (32.218.105.26, SNET), flat across all 24 hours. 39% of the entire site is three stuck loops on the card-test rig — a POST /user/login loop at 6,424/day that returns 200 every time and has never once authenticated (no Referer, no valid form token, so it never reaches login validation; watchdog records only ~110 real sessions/day site-wide), plus ~27-second polls of a tool permission and a card serial at ~3,100/day each. The UUID resolves to uid 3772 tester thetester — the test user the rig was moved onto on 2026-07-21, which fixed the member whose card was being charged and left the rig running. So the pages-served half of the overage is self-inflicted and needs no edge layer at all — stopping the rig is projected to take 1.15M → ~640k pages/month. The visits half is genuinely hostile traffic and does need the edge layer: 13,442 distinct non-building IPs in three days, 78% of them making exactly one request, 29% receiving only 403/404, each one billed by Pantheon as a visit. Also measured and worth acting on separately: the CDN cache hit ratio is 1.7–8.7%, so nearly every request reaches PHP — that is a performance finding, not a billing one, because Pantheon counts cache hits toward pages served. optimizable rather than planned because the watching half runs and the acting half was never built — the named next step is an edge layer, which also happens to be the only near-term way to restrict the unauthenticated door API (see API endpoint security) — and that step is now also unblocked, because the same logs answered the security ledger's standing question by identifying the reader egress IP The bot half was then measured too (same day), and every cheap mitigation is a dead end. robots.txt tops out at 3.9% of billed visits — 83% of the offending IPs present as ordinary Chrome and ignore it, and the declared crawlers are already obeying (7 of 1,790 touched a Disallowed path in three days). IP blocking is impossible: 8,753 IPs across 6,048 distinct /24s, with the top 50 /16 blocks covering 18.7% — a residential-proxy pool, not a range. And Drupal-side blocking moves the bill by exactly zero, because a 403 is still a request Pantheon served and counted. One real defect was found and fixed regardless: robots.txt ended with a second User-agent: group containing Allow: / after every Disallow, which last-match-wins crawlers read as permission to crawl everything. The lever that does work is a migration we simply have not done: the site still runs Pantheon's legacy Fastly CDN (via: 1.1 varnish), while Pantheon's next-generation Cloudflare-based GCDN went GA in July 2026 with bot protection included free and on by default — scoring each request, allowlisting verified crawlers, challenging the rest. Gated on two things: Terminus ≥ 4.1.9 (this machine is pinned at 3.6.2 by host PHP 8.1), and a written answer from Pantheon on whether challenged* traffic is still billed, which the docs do not state and which is the whole reason to do it. Recorded trap for whoever picks this up: our DNS is already at Cloudflare with the apex proxied, so orange-clouding www looks like a one-toggle fix and would genuinely stop the billing — but Pantheon discourages it and it breaks TLS certificate renewal, dropping the domain to Pending (Error).
Platform & MetaDocsTraffic overage analysis
info@ lands in a shared Google inbox answered mostly by Kate, and staff struggle to keep up with the volume
Outreach & Recruitment
stable → optimizable, and the human-sourced description above still holds; this is added evidence, not a rewrite. Retiring a tool is a status change on the tool (field_item_status → Gone or Storage) and that is the right source of truth, but nothing propagates it to the tool's badge. Four badges were still listed to members for machines that had left the building: Brother CNC Embroidery, Master Brewer, Screen Burning, and Sewing Machine Heavy Duty Singers — that last one had been masquerading as a broken booking path when in fact both Singers are gone. Members were passing quizzes for equipment we no longer own. optimizable rather than broken: disposal does what it was built to do and was never built further. The rule has to be every referencing tool, not any — 29 retired tools point at badges that are very much alive (five dead Monoprice and PowerSpec printers all point at "Prusa 3D Printers"), so retiring on a single Gone item would have wiped out live badges. drush appointment-facilitator:retired-badges --fix reports and repairs it, staged 2026-08-25 and rehearsed locally; still unbuilt is making it automatic on status change, so the badge retires with the last tool rather than waiting for someone to remember the command. A disposal checklist covering both records is the other half.
payment_updated row) but never losses: a member whose subscription cancels mid-recovery drops out of the snapshots with no row, so Confirmed Cancel read 0 for everyone since 2026-04-20 (28 real departures unrecorded; 17 had Christina's outreach within 30 days). Fix staged (makerspace_member_success 79c45cf): departures now auto-log a back-attributed confirmed_cancel, an ms:backfill-cancellations command repairs the gap, the resolution-rate card gained a names drill-down, and the Full Contact Log export now actually exports the log. Move to watch after deploy + backfill; verify Kate/Christina accept the corrected numbers SHIPPED LIVE 2026-08-21 and the backfill has run. ms:backfill-cancellations processed 27 departures on live (8 skipped as still-active members); the same command had been rehearsed on test with the same 27. Departures now auto-log a back-attributed confirmed_cancel, so Confirmed Cancel should stop reading 0. Still open and the reason this is watch not stable: Kate and Christina have not yet been told what the corrected numbers mean — resolution credit is back-attributed within 30 days, and "self-recovery" rows are members who paid with no human contact. Their reports were ledger #44334.
Membership & Billing
asset_log_entry holds 117 records and members report faults that feed the KPIs, with a tool-quality report checking configuration. The earlier "did not get traction" note was historical. New concern from the data: logging has fallen off a cliff — roughly 29/month across April–June, then 5 in July and 3 so far in August. Either faults dropped or reporting did, and those need different responses
Facilities & EquipmentDocsAsset maintenance queue (staff) · Tool quality report (staff)
/register/instructor, has dead-ended on "Page not found" since 2026-08-14. The account is created and the applicant is stranded on the next click — one confirmed casualty in the retained log window (uid 5733, the applicant the education manager was onboarding when they reported it). Two causes, either sufficient: the master agreement's alias moved to /instructor/agreement on 08-14, leaving the old /form/webform-5220 alive only as a redirect entity; and the redirect module refuses to act on any request carrying ?destination= (RedirectChecker::canRedirect()), which is exactly what the signup hand-off adds — so the redirect never fired. The deeper fault is that this was the one entry point the 08-13 rollback missed: it kept offering a legal agreement before any staff conversation, the specific thing the rollback removed everywhere else. It went unnoticed for three weeks because the path it offered it on was dead. Fix staged 2026-09-08, not deployed (profile_registration 523b359, instructor_companion 8d9ffaa): signups land on /become-instructor, which routes by role, and the post-signup copy no longer promises an agreement. The bigger finding is that the education manager was using that bookmark for a job the site never had a tool for — bringing in a person they had already met and decided will teach. Every door was built for a stranger arriving cold. Built the same day, staged in the same release: a staff-sent invite (/admin/education/invite) — one email, a 14-day link that signs the person in and opens the agreement, signing grants the role and opens the dashboard, outstanding invites listed with Resend. The old bookmark now goes to /become-instructor. Flow check the same evening (test env, 20/21 — the 1 is a stale script expectation): every self-serve step renders and a member proposal really saves, so the machinery is fine. The outcome is not: 0 member proposals in the 5 weeks since propose-first went live, 9 interest/idea submissions in 90 days and not one reviewed (oldest 06-18; the weekly nudge to education@ has changed nothing), the approval email has never been sent on live, 0 signings since May. The self-serve funnel is a working pipe nobody stands at the end of. Watching after ship: the first real invite end to end (email arrives, link works days later, the signed-agreement staff email reads correctly), whether Ashley's team reaches for the invite rather than the old bookmark, and whether the two queues get worked once the console is part of a routine SHIPPED LIVE 2026-09-09. /register/instructor now 301s to /become-instructor on live and the staff invite is live at /admin/education/invite. A second, hidden dead end was found on test and fixed in the same release, and it would have made the invite useless: login_destination's redirect_all_to_resources rule fires on every login with no page or role restriction, so it overrode the invite controller's redirect and landed the invitee on the Resources hub reading "Read the agreement below and sign at the bottom" on a page with no agreement. Pre-existing config, which is exactly why local runs never saw it and a live-cloned test did; the invite path is now exempted from the rule. The full walkthrough passes 28/28 on test — invite, email, signed link, agreement, signing, dashboard, role granted. Watching for: the first real invite Ashley sends end to end, and whether the two review queues get worked now that the console is one screen. The funnel's outcome problem is untouched by this release — 0 member proposals in 5 weeks, 9 unreviewed interest submissions, 0 signings since May — so a working pipe still has nobody standing at the end of it
Education & InstructionP2Grow
stable; a 2026-08-25 measurement showed it was not. Staff said at the 08-25 meeting that they submit things and never hear what happened. Of 324 submissions ever made, 139 (43%) had no entry on the board at all, the four honest-closure statuses had been used zero times in 174 items, and 82 open items had gone untouched for over three weeks. The mechanism was never broken — items that reached the board did get flipped on deploy — the failure was coverage and follow-through. Backfill took coverage 57% → 99% (302 items); 55 of the missing 139 turned out to be already fixed and nobody had said so. First full pulse under the new loop (2026-08-31) came back clean: every submission reached the board (2 unledgered, both known junk), the three oldest stalled items were given real answers rather than another status bump, and the three people who reported them were emailed automatically. One clean cycle is not yet a closed watch window — hold watch until a second. SHIPPED LIVE 2026-08-26: submitters now get a receipt and a status-change email, the form gained a staff alert (it previously had zero handlers, so submitting notified nobody), the board sorts by reported date, and weekly-triage gained a look-back step because step 1 only ever looked forward from the watermark — which is exactly how the gap opened. Two defects were caught on test before live: publish: false never worked at all (setPublished() takes no argument in D9+, so it silently published — all 303 ledger items and 124 changelog entries on live are published, nothing was ever held back), and the staff email re-sent on every manifest re-run. Watching for: whether the aging count actually falls, and whether the 66 not_actioned items get decided rather than becoming a new silent bucket. Cycle plan routing all 180 open items into 12 work packages at docs/ops/FEEDBACK_CYCLE_PLAN_2026-08-26.md. Originally: a well-documented A3 — worth copying as a pattern
Member Experience & RetentionP2Refine
connect.makehaven.org links across 10 nodes and 1 message template repaired, re-run reports 0, and /donate markup now contains none. Watching for a human confirming the Opportunity Fund card is clickable end-to-end; the Pantheon domain removal and the Cloudflare connect A record are still open. Prior state below. 2026-08-24 — one of the two donation routes on /donate has been dead, and nobody caught it. The Opportunity Fund card ("help make membership accessible to everyone") had its icon, heading and description all linking to connect.makehaven.org — the standalone-CiviCRM domain retired when Civi was merged into the main site. That host does not merely redirect badly: its HTTPS does not resolve at all (no certificate; it is the standing action_required in terminus domain:list) and plain HTTP returns Pantheon's 404 Unknown site. So anyone clicking that tile got a connection error, and it produced no server-side error on our side to find — the failure happened entirely in the donor's browser, which is why months of donate-page feedback circled the page without anyone naming this. Node 328 carried three more of the same links; 14 dead links in total across 10 nodes and one CiviCRM message template. Paths map 1:1 (www.makehaven.org/civicrm/contribute/transact?reset=1&id=2 returns 200), so the repair is a host swap: config fix for the staff CRM-contact view plus an idempotent content port, staged 2026-08-24, deploy pending — it writes content, so it must run on test and live. Moves back to stable once the card is verified clickable on live. Found while retiring the dead domain, not by anyone testing the donate page. The acknowledgement half is unaffected and still works: The acknowledgement procedure is followed. Some manual steps are deliberate — handwritten notes on cards — and JR sees room to automate acknowledgements for smaller donations while keeping the personal touch for larger ones.
Finance & AccountingDocsDonation Acknowledgement Procedure
/admin/education console — the half that survived the rollback
Education & Instruction
scripts/workflow-health.sh on 2026-08-17 to close that: one for a smart group whose cache stops rebuilding, one for a group fed by an automated write path that has gone quiet. The second immediately named a real outstanding case the audit had left behind — the Guest Waiver webform's group-add has been dead for 145 days, harmless only because that audience is now activity-driven. Leaves watch when a freeze is caught by the check rather than by a person
Communications
changing → watch. Outreach reporting dates aligned, first-contact-to-first-positive timing now counts all contacts rather than ignoring earlier unsuccessful ones, and obsolete latest-flags are cleared after the daily snapshot completes. Watching whether the daily run stays clean and whether the corrected timing changes what staff conclude about which interventions work.
Member Experience & Retention
borrowed_or_missing_without_borrower integrity check conflates "borrowed without a borrower" (a real fault) with "missing without a borrower" (usually expected), and reads as failing because of a stale write-off backlog nobody has triaged — so the one instrument pointed at this process cannot currently tell a fault from housekeeping
Lending, Storage & Store
scheduled_date NULL and no approval, so the draft is inert (note, corrected 2026-09-05: a job-row count is NOT the safety test — a test send creates a job row and computing recipients fills civicrm_mailing_recipients, which together read like a premature submission. The honest test is scheduled_date and approval_status_id both NULL with no non-test recipient queued); monthly_active is false, so our own scheduler ignores it. Draft-only cron shipped live 2026-09-09, flag still deliberately OFF. The reason it could not be switched on was code, not editorial: cron called the create-and-send path under a different mailing name from the draft staff edit, so flipping it would have mailed the raw template to ~6,000 people and left the edited issue behind. The monthly path now only builds a draft and emails alert_email a link. Proven on test before shipping, against the honest test this row already insists on: with the flag forced on, cron created a mailing with scheduled_date NULL, approval_status_id NULL and zero job rows, and cron correctly skipped while that month's issue already existed. A near-miss worth recording: committed config carried alert_email: '' against live's staff@makehaven.org, so the config import would have blanked the very address this feature emails — caught during reconciliation, and the feature would otherwise have shipped mute. monthly_active is now safe to flip and remains JR's call; flipping before 2026-10-01 points the first run at September's already-sent issue too. It was created this way deliberately: drush mh:digest-create-monthly --force has no draft stage — DigestScheduler calls submitMailing() then processMailingNow() in one execution and backdates the schedule, so the documented "first send" path would have mailed 6,600 people with nothing a human could inspect in between (and it still errors on test, since 2026-08-18). JR's call 2026-09-03: a human approval step until this is dialled in. Two things landed the same day that make an issue writable at all: a Notice content type (/node/add/notice) so staff write member news as site content that feeds both newsletters from one source instead of typing it into Mailchimp, and a newsletter console at /admin/makerspace/newsletter with a preview that renders the real email and can send a test copy — previously the only way to see an issue was to mail it. Both previews render against live data with 0 unresolved tokens. A moves 2 → 3 and D 2 → 3: the issue can now be seen and tested before it goes, which was the missing feedback loop. Watching: the Saturday 2026-09-06 send — whether Kate can edit, preview-as-a-contact and test without help, and what delivery looks like against the weekly baselines. Open: --no-send on both create commands and taking the send out of --force for the monthly, so the accidental mass-send path stops existing rather than being avoided by convention. Preview as a specific contact, never as yourself — rendered_view is interest-filtered, so a staff copy is not representative.
Communications
/appointment/request/{badge} and a badger accepts, declines, or counter-proposes a time. 2026-08-25, and the correction matters: this loop is not dead. The first pass marked it broken on the strength of a 403 on /badger/dashboard plus a permission grep. Reading the routes shows the badger is emailed accept / decline / counter links and those routes are _access: 'TRUE' token-guarded, so a badger can answer a request today from the email, whatever their role. What was genuinely missing is the standing overview: /badger/dashboard, which lists everything waiting on them, returned access denied to every facilitator because both its permissions were administrator-only. A badger who lost the email had nowhere to look. optimizable rather than broken because the process does what it was built to do and was never built out — the overview is the unfinished half. Fixed 2026-08-25 (config, staged not deployed): view on request badger dashboard and create appointment for member granted to the facilitator role; both permissions' own descriptions name facilitators as the intended audience. The real limit on this process is not permissions at all: exactly one coordinator profile has field_coordinator_on_request set, so the path is barely offered. Open decision — is on-request opt-in per facilitator, or the default for anyone who issues a badge nobody keeps hours for? Scored A3/D1 deliberately: a human starts and drives each instance, and nothing describes it.
Education & Instruction
supplier= param. Deliberately not yet reported to the member who found it: verified anonymously only, and ledger items 44325 / 44550 / 44551 stay open until a logged-in look at /store confirms it. Prior state below. Member plywood complaint 08-17 confirmed /store rendered only classic-PayPal buttons (now behind a PayPal auth wall; sales 168/mo Apr → 28 by mid-Aug) with zero links to the tab/Stripe checkout. Fix shipped live 2026-08-20 (config-only): every buy button on /store, material pages and related-supplies blocks now routes to /store/checkout-item/{nid} with a guest-PayPal fallback. Verified on live 2026-08-20 — the staged config is deployed (the local-vs-live config diff no longer contains it). One day of data, too early to call: in-product transactions ran 43 in July and 57 so far in August, and the day after the deploy carried 6 against a recent daily median of 2. Re-measure the PayPal-vs-Stripe split and tab uptake in the week to 2026-08-27 before moving this to stable. 2026-08-24 weekly pulse — the fix itself regressed, and a member found it before we did. The deployed config builds the guest link as href="…webscr?{{ nothing_2 }}", and nothing_2 composes its query string with item_name={{ title }} — but title on this view is the linked title field, so its rendered value is a whole <a href="…">Item Name</a>. The inner quote closes the href early: the guest link points at a truncated, unusable PayPal URL, and the remainder of the query string renders as visible raw text under every Purchase button on /store, on material pages, and in Related Provided Supplies (four display sites). The member-facing Purchase button to /store/checkout-item/{nid} is unaffected and still works, so this is broken for the guest path and ugly for everyone, not a total outage. It never showed before because nothing_2 had only ever been consumed through Views' path: setting, which URL-encodes it. Reported as feedback sid 20328 on 2026-08-21, the day of the deploy; root-caused in the 2026-08-24 pulse; entry and three fix options in DEPLOY_TODO, public ledger #44550. The measurement above is now confounded — any PayPal-vs-Stripe split read in the week to 08-27 is a read of a broken guest link, so re-measure only after the fix. Two related store items came in from the same reporter the same day: material pages reached from /store carry no Request Restock button (ledger #44551), and restock-request emails intermittently omit the supplier and material link buttons — a re-report of the same complaint from 2026-05-11 that was never fixed (ledger #44552).
Lending, Storage & Store
stable 2026-08-14) 2026-08-25 — the revocation half does not reliably happen, and the rest of the flow works so well that it hid this. Kate asked why a cancelled member's box was still ticked (Albert Pimpinella, uid 5696). The webhook had fired and recorded the reason and end date correctly; it removed no role, because it only ever looked for member and he was still member_pending_approval — he had signed up the day before. The removal logged only on success, so the miss left no trace; the approval a day later then granted him membership, and with an active door badge and no pause flags he passed every access gate. He was still badging in two weeks later with Chargebee reporting mrr: 0. Nothing disagreed afterwards either: no job asked whether everyone holding the role was actually paying. Asking Chargebee directly on 2026-08-25 found 12 accounts holding a membership role with no live subscription, each individually confirmed cancelled; 8 of them can currently open the door. Counting from our own fields found only 5 of the 12 — three have no membership end date recorded at all, four hold only member_pending_approval so a roles = member query never sees them, and one carries a future end date. Our own data could not have produced the list. Impact raised 3 → 4: this is door access to an unstaffed 24/7 space plus uncollected dues, not internal friction. Fix SHIPPED LIVE 2026-08-26 (cancellation strips onboarding roles too, a warning when a lapsed account is granted the role, and a nightly audit against Chargebee). 2026-08-27 — detection is now proven and the remaining gap is a decision, not code. The audit's first nightly run on live (08-26 21:34) checked 864 members and named the same 12, unprompted. Kate ruled on the list the next day: four are genuinely lapsed and still hold the role — uids 2962, 5186, 5225, 5696, every one of them with an active door badge — and the other eight were intentional or have since been remedied. A read-only run on live that day returned 858 checked, exactly those 4, all status=cancelled in Chargebee — no false positives. The operating rule is JR's: Chargebee is the authority — an active plan means access, a deactivated one means none — and that is already what the audit implements, since it counts active, in_trial, future, non_renewing and paused as live. A plan-name carve-out exempting invoiced accounts was written and reverted the same day: it assumed those meant membership with no subscription behind it, but uid 5576 is active and uid 719 non_renewing, so neither was ever at risk, and matching on plan name would have kept an invoiced member in forever once their subscription was cancelled. Two of Kate's four (uids 2962 and 5186, cancelled in April) have no end date and no cancellation reason recorded at all — for those the webhook never wrote anything, so the role-stripping fix would not have caught them either; only an audit that asks Chargebee directly finds that shape. Two integrity guards staged 2026-08-27 for the day it is allowed to act: a truncated Chargebee fetch aborts instead of reading unfetched pages as nobody-paying, and an implausibly large lapsed count reports without acting. Still broken: four people can open the door today without paying. It closes when JR authorises drush chargebee:audit-lapsed --revoke on live and the auto-revoke flag is flipped
Membership & BillingDocsMember success dashboard (staff)
SelfSvcUpdate form accepts only a valid checksum or the edit all events permission — being logged in as the participant does not count. The cancel link is generated inside the registration confirmation email, and checksum_timeout has no row in civicrm_setting, so we run CiviCRM's 7-day default. Measured over 180 days on live: 579 of 960 registrations (60%) are made more than 7 days before the event, mean gap 17 days, max 281 — so for most people the link is already dead when they reach for it, and whether it works correlates with how early they booked. The sharp edge is that the lifetime is baked into the checksum at generation, so raising the setting fixes only newly sent links; every confirmation already in an inbox keeps 7 days forever and staff must cancel those by hand. Fix (raise to 60 days) staged 2026-08-24 — it is a CiviCRM setting, so it does not travel with drush cim and must be set per environment, the same trap that hid the W34 digest stall. FIXED ON TEST AND LIVE 2026-08-24 — checksum_timeout raised 7 → 60 days. Live went from days=7 life=168 to days=60 life=1440, the DB row reads i:60;, and an end-to-end simulation on live (a link minted under the new policy and back-dated 10 days) validates — the exact case that had been failing. watch, not stable, for two reasons: the fix is forward-only, so every confirmation email sent before 2026-08-24 keeps its 7-day life and staff must still cancel those by hand until those events pass (JR decided against re-sending confirmations); and the value lives in civicrm_setting, which terminus env:clone-content overwrites — live now holds the right value so a clone propagates it, but any future environment rebuild should be re-checked with scripts/civicrm/set-checksum-timeout.php. Moves to stable once staff confirm the reports have stopped. Public ledger #44560, flipped to shipped. D scored 1, not ?: nothing described this behaviour and staff had no way to tell a real permissions fault from an expired link
Education & Instruction
system_cron completed 24 passes on every full day in the retained window. What is missing is anything watching it. The launcher logs "No free threads available for launching jobs" roughly 57 times a day, every day (554 across nine days). That is invocations arriving while a pass is still running rather than the hourly pass being lost, but it means a real stall would look exactly like the noise we have learned to ignore. Separately makerspace_ai_tool_context_cron has sat disabled since 2026-07-08 and nothing reported it. The end state someone can name: a job that misses its own window tells a person, instead of a person noticing
Platform & Meta
field_member_goal — inventor / entrepreneur / seller. 46% of new members identify this way
Entrepreneurship
webform_497's acknowledgement handler addressed […values:e_mail_address:raw] while the element is e_mail_address_25, so it resolved empty and Webform declined to send — for every submission the form has ever taken. The token is now correct on live and both handlers fire. A re-scored 1 → 4 in one step: the old 1 scored the outcome (no acknowledgement ever arrived), not the mechanism, which was always an automatic Webform email with one wrong character. Two things the fix does not cover — the 129 people who submitted and heard nothing have still not been told, and the form requires topic images, machines and consumables, so a guest speaker offering a talk cannot submit it honestly
Education & Instruction
ai_logging writes output_text from the provider's raw output and the OpenAI chat provider leaves it empty — every chat row on live is literally {}, so the AI oversight console has been showing blank replies and answer quality was unmeasurable except when a member happened to ask a follow-up and the previous turn came back as conversation history. Which assistant answered and which user asked were not recorded either. Fix staged 2026-08-20 (makerspace_ai_tool_context 6a34121): the answer, a refusal flag, the assistant id and the uid now go into the log's extra_data. (2) The quality problem is concentrated and countable. Of the navigator conversations where the member sent a second turn, roughly 70% followed the assistant saying "I'm not sure" or "I don't know" — the follow-up is usually a member re-asking after a failure, not deepening a good answer. Asked how to submit a meetup it pointed at the CiviCRM event browser; the member replied "The answer is makehaven.org/meetup — learn that" and it said "I'm not sure" again. (3) The demand is wayfinding, not tool expertise. 24 navigator questions since 2026-07-01 cluster into five intents — room and door access (7), membership change (4), booking and facilitator hours (4), stuck in onboarding (3), profile editing (2) — while the tool-page assistant, which is on every tool and badge page for members and anonymous visitors alike, draws about three questions a month. The reach gap is the actionable half: the navigator is placed on six path patterns (/resources, /my-membership, /badges, /user/*) and none of /access-request, /appointments or the onboarding pages are among them, so a member sitting on the page where the question arises has nothing to ask. Nobody has ever rated an answer — the feedback table has 8 rows and has not moved since 2026-07-16. Separately, "Red", the anonymous recruitment assistant built to answer prospective-member questions without login, is switched off on the theme anonymous visitors actually get while still enabled in a theme the site does not use
Platform & Meta
The ranking above is arithmetic — impact multiplied by how manual something still is. It cannot know what staff have noticed. These 8 were flagged by a person, and that is a different and often better signal.
The full reference. The Auto, Doc and Impact scales, and how much of this is verified, are in the two boxes below — open them when a number needs defining; the list itself starts right after.
Three axes, defined tightly enough that two people should land on the same number. If they don't, the fix is the wording of the scale — not the score.
Automation and Documentation run 1–5 and read the same way, so a 2 means the same distance from finished on either. They used to be A1–A5 against D0–D3, which made the two columns look comparable while measuring on different rulers. The top bands are the matched pair worth noticing: A5 is "runs itself and tells us when it fails", D5 is "written down and we would know if it went stale." Both are empty. Impact is a 1–5 scale too, but it is not a maturity axis — it says how much a shortfall on the other two would cost.
Code counts as documentation. An earlier version of this scale filed code alongside Slack threads, which scored our best-described processes as though nobody had written anything. A maintained module is a better description than most SOPs: it is precise, and it cannot drift from what actually happens, because it is what happens.
So a process that runs as code — A4 or above — and names the module that runs it is floored at D3: the implementation is the current description. 26 processes are raised by this rule. It applies only from A4; at A3 and below a human still drives steps the code does not capture, so the tooling does not describe the whole process.
Naming the module is part of the rule, not a formality. It used to raise any A4 row, and half of those named no module anywhere — so the registry claimed a description existed while pointing at nothing. Sliding-scale membership was the clearest case: A4 because the join form is automated, floored to "a durable description exists" when who qualifies is written down nowhere. An A4 score says a process runs itself; only a module says where to read what it does.
What code does not earn is D4. That still takes a second person working from it unaided — and by that test the codebase is as unproven as the paperwork.
The gap that matters most is A4 → A5, and the registry states it plainly: 52 processes run themselves; 1 also tell us when they stop. "Runs itself" and "tells us when it stops" are different products, and every automated process here is currently the first without being the second — so the way we find out that something broke is that a person notices.
All 212 rows, grouped operationally and mapped to the strategic plan's six goals. ? means genuinely unknown — please do not guess it in review, ask the person who does it. There is no owner column: most of what happens here is collaborative or automated, and a single name per process described neither who does the work nor who can judge whether it is right.
Auto12Doc12 1 and 2 are flagged, on both. Auto and Doc run 1–5 the same way — low is bad — so one rule covers them, red for the worse of the two. Everything else stays plain ink. Impact carries no colour: a high impact is not a fault, it is what decides whether a deficit is worth fixing. I4 and I5 sit in stronger ink for that reason, not as a warning.
| Process | Auto | Doc | Impact | State | Notes |
|---|---|---|---|---|---|
| Instructor recruitment funnel | 3 | 2 | 3 | watch | Menu consolidation shipped 08-12, reverted 08-13 after staff objection. No CiviCRM link on instructor interest. Found broken 2026-09-08 by the education manager, not by us: the self-registration entry point staff have used for years, /register/instructor, has dead-ended on "Page not found" since 2026-08-14. The account is created and the applicant is stranded on the next click — one confirmed casualty in the retained log window (uid 5733, the applicant the education manager was onboarding when they reported it). Two causes, either sufficient: the master agreement's alias moved to /instructor/agreement on 08-14, leaving the old /form/webform-5220 alive only as a redirect entity; and the redirect module refuses to act on any request carrying ?destination= (RedirectChecker::canRedirect()), which is exactly what the signup hand-off adds — so the redirect never fired. The deeper fault is that this was the one entry point the 08-13 rollback missed: it kept offering a legal agreement before any staff conversation, the specific thing the rollback removed everywhere else. It went unnoticed for three weeks because the path it offered it on was dead. Fix staged 2026-09-08, not deployed (profile_registration 523b359, instructor_companion 8d9ffaa): signups land on /become-instructor, which routes by role, and the post-signup copy no longer promises an agreement. The bigger finding is that the education manager was using that bookmark for a job the site never had a tool for — bringing in a person they had already met and decided will teach. Every door was built for a stranger arriving cold. Built the same day, staged in the same release: a staff-sent invite (/admin/education/invite) — one email, a 14-day link that signs the person in and opens the agreement, signing grants the role and opens the dashboard, outstanding invites listed with Resend. The old bookmark now goes to /become-instructor. Flow check the same evening (test env, 20/21 — the 1 is a stale script expectation): every self-serve step renders and a member proposal really saves, so the machinery is fine. The outcome is not: 0 member proposals in the 5 weeks since propose-first went live, 9 interest/idea submissions in 90 days and not one reviewed (oldest 06-18; the weekly nudge to education@ has changed nothing), the approval email has never been sent on live, 0 signings since May. The self-serve funnel is a working pipe nobody stands at the end of. Watching after ship: the first real invite end to end (email arrives, link works days later, the signed-agreement staff email reads correctly), whether Ashley's team reaches for the invite rather than the old bookmark, and whether the two queues get worked once the console is part of a routine SHIPPED LIVE 2026-09-09. /register/instructor now 301s to /become-instructor on live and the staff invite is live at /admin/education/invite. A second, hidden dead end was found on test and fixed in the same release, and it would have made the invite useless: login_destination's redirect_all_to_resources rule fires on every login with no page or role restriction, so it overrode the invite controller's redirect and landed the invitee on the Resources hub reading "Read the agreement below and sign at the bottom" on a page with no agreement. Pre-existing config, which is exactly why local runs never saw it and a live-cloned test did; the invite path is now exempted from the rule. The full walkthrough passes 28/28 on test — invite, email, signed link, agreement, signing, dashboard, role granted. Watching for: the first real invite Ashley sends end to end, and whether the two review queues get worked now that the console is one screen. The funnel's outcome problem is untouched by this release — 0 member proposals in 5 weeks, 9 unreviewed interest submissions, 0 signings since May — so a working pipe still has nobody standing at the end of it |
| Instructor proposal review | 3 | 2 | 3 | watch | /admin/education console — the half that survived the rollback |
| Instructor agreement signing | 2 | 2 | 3 | changing | Now staff-sent; orientation video + quiz switched off. 2 quiz questions contradict our own docs |
| Instructor interest acknowledgement | 4 | 1 | 3 | watch | Fixed live 2026-08-13, re-verified against live 2026-08-17. webform_497's acknowledgement handler addressed […values:e_mail_address:raw] while the element is e_mail_address_25, so it resolved empty and Webform declined to send — for every submission the form has ever taken. The token is now correct on live and both handlers fire. A re-scored 1 → 4 in one step: the old 1 scored the outcome (no acknowledgement ever arrived), not the mechanism, which was always an automatic Webform email with one wrong character. Two things the fix does not cover — the 129 people who submitted and heard nothing have still not been told, and the form requires topic images, machines and consumables, so a guest speaker offering a talk cannot submit it honestly |
| Instructor development & peer observation | 1 | 1 | 3 | planned | A way for instructors to keep improving after they start teaching — peers sitting in on classes and feeding back to the instructor. Nothing built.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Instructor evaluation & coaching | 2 | 2 | 3 | optimizable | The education manager reviews evaluations as they arrive, but JR: "we can do much better to aggregate, and actually to get people to do the evaluations." Both halves — response rate and trend analysis — are weak |
| Class scheduling & publishing | 2 | 2 | 4 | changing | The education manager engages a contractor who schedules. JR has self-serve scheduling in the pipeline, particularly for repeat instructors |
| Class registration | 4 | 1 | 4 | watch | CiviCRM. 2026-08-24 — self-service cancellation is broken for roughly 60% of registrations, and it had been read as random. Staff (Ashley Zdeb) reported members hitting "You do not have sufficient permission to transfer/cancel this participant" "sporadically". It is not a permissions problem: CiviCRM's SelfSvcUpdate form accepts only a valid checksum or the edit all events permission — being logged in as the participant does not count. The cancel link is generated inside the registration confirmation email, and checksum_timeout has no row in civicrm_setting, so we run CiviCRM's 7-day default. Measured over 180 days on live: 579 of 960 registrations (60%) are made more than 7 days before the event, mean gap 17 days, max 281 — so for most people the link is already dead when they reach for it, and whether it works correlates with how early they booked. The sharp edge is that the lifetime is baked into the checksum at generation, so raising the setting fixes only newly sent links; every confirmation already in an inbox keeps 7 days forever and staff must cancel those by hand. Fix (raise to 60 days) staged 2026-08-24 — it is a CiviCRM setting, so it does not travel with drush cim and must be set per environment, the same trap that hid the W34 digest stall. FIXED ON TEST AND LIVE 2026-08-24 — checksum_timeout raised 7 → 60 days. Live went from days=7 life=168 to days=60 life=1440, the DB row reads i:60;, and an end-to-end simulation on live (a link minted under the new policy and back-dated 10 days) validates — the exact case that had been failing. watch, not stable, for two reasons: the fix is forward-only, so every confirmation email sent before 2026-08-24 keeps its 7-day life and staff must still cancel those by hand until those events pass (JR decided against re-sending confirmations); and the value lives in civicrm_setting, which terminus env:clone-content overwrites — live now holds the right value so a clone propagates it, but any future environment rebuild should be re-checked with scripts/civicrm/set-checksum-timeout.php. Moves to stable once staff confirm the reports have stopped. Public ledger #44560, flipped to shipped. D scored 1, not ?: nothing described this behaviour and staff had no way to tell a real permissions fault from an expired link |
| Class promotion / seat fill | 4 | 2 | 3 | watch | Targeted empty-seats email still unbuilt |
| Event capacity marketing | 4 | 3 | 2 | stable | Early Bird / Flash Sale |
| Class attendance marking | 2 | 1 | 3 | watch | Discovered 2026-08-17 by the weekly pulse — nobody had a row for it. Recording who actually turned up to a class, which is what separates a registration from an attendance and feeds the education KPIs and the post-class follow-up. The purpose-built instructor path — the "Take attendance" screen in instructor_companion — is a hard fatal and has never worked: AttendanceForm::buildForm() calls $this->entityTypeManager(), which FormBase does not provide, so every instructor who clicks it gets a white screen. Six real attempts in the retained log window. It is not wholly unmarked, because staff can set participant status by hand in CiviCRM: over the last 30 days 12 marked Attended and 2 No-show against 110 still sitting at Registered, so the fallback runs but rarely. Fix staged 2026-08-17 (instructor_companion 19bae63), deploy pending — the form now injects the entity type manager it was calling as a helper, verified locally by building and submitting against a real past class. The fix reached live on 2026-08-20 and the fatal is gone — live composer.lock pins 19bae637, and the last AttendanceForm::entityTypeManager() error in live watchdog is 2026-08-18, before the deploy. The outcome has not moved: over the 30 days to 2026-08-20, 119 Registered against 12 Attended and 2 No-show — the same ratio as before the fix. So the dead tool was never the whole problem; nothing prompts an instructor to open the screen, and the post-class reminder does not ask for attendance. Watching whether the unmarked count falls now that the form works; if it has not by mid-September, the next move is a prompt, not a repair. Scored A2 rather than A3 because the tooling half is dead and the surviving path is a human editing CiviCRM records, and D1 because nothing describes that fallback |
| Class evaluation collection | 4 | 3 | 2 | watch | Applied live 08-11. NPS 76.4 but plan notes "low response rate for number of potential respondents" |
| Workshop fill-rate management | 3 | 2 | 4 | stable | The education manager watches fill rates; undersubscribed classes get automatic promotion in the newsletter, with automatic discounting intended later. 55% weighted fill against an 80% goal. |
| Instructor stipend payment | 4 | 3 | 3 | stable | Better automated than assumed. Instructors submit hours on the instructor dashboard after class; those import to Xero/Melio and staff approve. JR: still being refined, but working. Module attached 2026-08-28: the hours-to-Xero half of this is xero_bills_sync's timesheet path, so Q1 2026 development on that module moved this process too — it was not visible here before because the row carried no code field |
| GEMS cohort management | 2 | 2 | 3 | stable | Running now, with details in the event listing |
| Badge quiz authoring | 2 | 2 | 3 | stable | Mostly written by the shop manager, with a badge-quality report used to find problems — and acknowledged work to do thereRaisedStaff, 2026-08-14 — part of the badge-earning flow, which staff see as a major driver of retention and a strong candidate for review soon |
| Badge grant from quiz | 4 | 3 | 4 | changing | Reviewed 2026-08-24 by walking the flow end to end (both viewports, both paths, real 100% passes) — the review staff asked for below. The grant itself is sound: a no-checkout badge goes ACTIVE the moment the quiz is passed, and a checkout badge correctly lands PENDING rather than granting early. Marked optimizable rather than stable because the hand-off after the quiz was never finished, and that is where the flow leaks. Two concrete gaps. The member is never told how long a checkout takes: the time is recorded on 71 of the 73 checkout badges, averaging about 34 minutes, and the module exposes it as a [badge:checkout_minutes] token that its own shipped default template prints — but the customised template saved in assign_badge_from_quiz.settings dropped that line, so the one number a member needs to judge whether a checkout fits into today's visit is collected for almost every badge and shown for none. One line puts it back. (The checklist button beside it is a different matter: the template still handles it, but only 10 of 73 badges have a checklist URL to link to — missing data, not missing markup.) And nothing chases a badge that stalls. 5,107 badge requests sit PENDING on live. The pile is not the signal — 83% of it is over a year old and largely belongs to people who are no longer members — but the recent cohort is 80 requests from 49 members in the last 30 days, and nothing measures whether those convert, nudges the member, or would show the rate moving. The onboarding funnel got exactly that treatment once the same problem was measured there. What the panel does well is worth saying too: it shows a real timetable of who is in the building, with a Schedule button per shift and a card per facilitator. Walkthrough: https://claude.ai/code/artifact/1738fd87-b82c-4c86-8a86-76d50bf7bf18RaisedStaff, 2026-08-14 — part of the badge-earning flow, which staff see as a major driver of retention and a strong candidate for review soon Two-stage prerequisites staged 2026-09-10 (JR, following Kate’s concern): pending-or-earned prerequisites permit advanced requests and checkout bookings; earned prerequisites are required for final staff/class/automatic approval. Approve basic then advanced during one visit. Existing earned badges are preserved. Live audit found missing sewing definitions and approval gaps; local database, browser, quiz/class and on-request checks pass. Four sewing prerequisite definitions are prepared with an idempotent content step. Not deployed; observe joint-checkout success and refusal of premature awards after the next bundled release. |
| Badge checkout appointment | 4 | 3 | 4 | watch | A member books a one-to-one slot with a badger to be checked out on a tool and earn the badge. Runs automatically; a fault that broke the 60-minute slot length was fixed in July 2026. Cancelling a booking has never returned the slot to the pool — reported by staff 2026-08-19 (feedback sid 20312) and confirmed in code and data by the 2026-08-20 cycle review. The availability view carries exactly one cancellation filter and it reads field_reservation_cancellation, a vestigial field from the older reservation content type that none of the 914 cancelled appointments on live carry; all 914 stay published and keep their slot blocked. The booking form's JavaScript builds its list of taken slots straight from that view, so the lost capacity is invisible rather than merely cosmetic. Volume is steady rather than dramatic — about 31 cancellations per 30 days — which is why it went unnoticed for so long. Fix staged 2026-08-20 (config only: the view now filters field_appointment_status != canceled), deploy pending; verified against the real view query, which excludes the cancelled node and still blocks scheduled ones. Noted while fixing and not shipped: the view still pages at 15 rows sorted newest-first, so a facilitator with more than 15 appointments may not have every reserved slot rendered. Second, separate fault found 2026-08-21 from Lior's water-jet report: the stored time can be hours off the real session. The time is computed as posted-shift-start + slot offsets, and the booking link's start_time was carrying the shift's end — every one of one badger's bookings sat exactly one shift-length late (a checkout displayed 7:30pm for a 3–6pm shift), while a bare H:MM with no meridiem parsed as the small hours for five others. 47 appointments across 2026 affected. Fix staged 2026-08-21: the slot base is now reconciled against the shift the host actually posted, plus drush appointment-facilitator:retime-appointments --apply to repair the stored ones and audits to keep it visible. Arrival statuses were classified against the wrong times and need recomputing after the repair Both faults SHIPPED LIVE 2026-08-21 (release 2026-08-21-member-success-storage-appointments-batch). The cancellation filter now reads field_appointment_status != canceled, and the retime repair ran on test and live: retime-appointments --apply corrected 47 appointments across 2026 (identical count on both environments), arrivals were recomputed, and audit-times --since=2026-01-01 now reports every appointment inside its facilitator's posted hours. Watching for: a scheduled appointment must still block its slot, and the 15-row pager limit noted above is unfixed. Cosmetic bug found while running it — backfill-arrivals logs "Updated: @updated. Skipped: @skipped" with unreplaced placeholders, so the real counts are lost. Separate gap found 2026-08-24 by the badge-earning flow walk, one layer up from those repairs: nobody is reminded that a booked checkout is happening. appointment_notifications.settings.reminder_enabled is false on live and all four reminder templates — member and host, subject and body — are empty strings, though reminder_days_before is already set to 1. Both sides are emailed when the appointment is made and then hear nothing until it is time. The mechanics of booking have now been fixed twice; what has never been built is the thing that gets both people to turn up, which matters because the characteristic failure of this process is a no-show and a no-show is what leaves a badge stuck at pending. Needs copy for four fields plus the switch — no code. Done 2026-08-25 (config, staged not deployed): reminder_enabled on, and member and host reminder templates written — the member gets when, who, which tools and a cancel link that now genuinely releases the slot; the facilitator gets who is coming plus a prompt that the badge is still not granted until they approve it. Calendar invites needed nothing: calendar_invites_enabled is already true and invites attach at booking (member_scheduled / host_scheduled / canceled), which is the right moment — a reminder does not re-attach one. Both emails were rendered and sent through the real handler locally with every token resolving; the cron selection path is not yet exercised (the local Docker daemon dropped first) and is listed as a test-env check in DEPLOY_TODO rather than assumed.RaisedStaff, 2026-08-14 — part of the badge-earning flow, which staff see as a major driver of retention and a strong candidate for review soonDescription inferred 2026-08-15 Third gap found 2026-08-25 by the staff badge feedback session, and it is upstream of everything above: for some tools there is nobody to book at all. The repairs so far have all been to the mechanics of booking — slot length, cancellation, stored times, reminders — while 9 checkout badges have 0–1 currently-active issuer and 159 members waiting between them. A member on one of those tools does everything right and sees an empty schedule, which reads as the site being broken rather than as nobody being available. Two tools have no active coverage whatsoever: Blind Stitch Hemmer (24 waiting) and Vacuum Forming (7). Tracked as its own process — see Badge coverage review — because the fix is recruiting, not code. Also staged 2026-08-25 and not yet live: appointment-facilitator:unbookable-badges, which names any checkout badge with no reachable issuer so this stops being found by a member standing in front of a machine. — not confirmed by anyone who runs it |
| Badge issuance after a class | 2 | 1 | 4 | watch | Added 2026-09-04 — the class-based route to a badge had no row; only the facilitator-appointment route did. A class that lists a badge (field_civi_event_badges) includes the badging session and the instructor is the badger, so the instructor's sign-off should be the checkout. In practice nothing happened after class: participants stayed Registered (no Attended on any 2026 lathe class), the instructor "class checkout" page had been used once, ever, and even when used it only stamped a date — the badge still needed a separate facilitator checkout. So students did what the badge page told them: 6 of 8 Metal Lathe and 3 of 5 Bridgeport training-documentation submissions since May came from class registrants, each landing in JR's inbox as "Documentation review". Three 2026 lathe classes (~14 students) produced 1 active badge. Staged 2026-09-04, not deployed (release 2026-09-04-class-checkout-issues-badge): the instructor's click now marks Attended and activates the badge (or activates it on the quiz pass), a rare "attended, did not pass → retake" action exists, and class registrants are no longer asked for documentation. Also found: the badge page's class-registration bypass and its "Upcoming classes" list had never worked (entity query on a CiviCRM-entity Drupal field returns nothing). Watching for after ship: Brandon and Ken actually using the page at the end of class (post-event reminder already links it), documentation-queue volume, and Attended counts on badge classes. SHIPPED LIVE 2026-09-09. Watching exactly what the row already named: whether Brandon and Ken use the page at the end of class, whether the documentation-review queue stops filling with class registrants, and whether Attended counts move on badge classes. A2 because the page existed but did nothing useful; D1 because no doc said the class is the checkout until the module README §4 written today |
| On-request badge checkout | 3 | 1 | 3 | optimizable | Discovered 2026-08-24 by the badge-earning flow walk — nobody had a row for it. The fallback for a badge whose issuers keep no regular hours: instead of picking a posted shift, a member asks at /appointment/request/{badge} and a badger accepts, declines, or counter-proposes a time. 2026-08-25, and the correction matters: this loop is not dead. The first pass marked it broken on the strength of a 403 on /badger/dashboard plus a permission grep. Reading the routes shows the badger is emailed accept / decline / counter links and those routes are _access: 'TRUE' token-guarded, so a badger can answer a request today from the email, whatever their role. What was genuinely missing is the standing overview: /badger/dashboard, which lists everything waiting on them, returned access denied to every facilitator because both its permissions were administrator-only. A badger who lost the email had nowhere to look. optimizable rather than broken because the process does what it was built to do and was never built out — the overview is the unfinished half. Fixed 2026-08-25 (config, staged not deployed): view on request badger dashboard and create appointment for member granted to the facilitator role; both permissions' own descriptions name facilitators as the intended audience. The real limit on this process is not permissions at all: exactly one coordinator profile has field_coordinator_on_request set, so the path is barely offered. Open decision — is on-request opt-in per facilitator, or the default for anyone who issues a badge nobody keeps hours for? Scored A3/D1 deliberately: a human starts and drives each instance, and nothing describes it. |
| Badge coverage review | 2 | 2 | 3 | planned | Discovered 2026-08-25 by the staff badge feedback session — nobody had a row for it, and nothing does it today. Staff went to badge a member on the Blind Stitch Hemmer and found no way to book a checkout; the tool page showed nobody. The cause was not the machine. Being named in a badge's field_badge_issuer records that a person is qualified on that tool, and JR's rule is that the naming is permanent — the skill does not leave them, so if they return later as a facilitator or instructor we already know what they can check out. The issuer list is therefore a standing recruiting pool and must never be pruned to match the current facilitator roster. What nothing surfaces is the gap between the two: which tools have members waiting and nobody currently covering them. Measured for the first time 2026-08-25: 9 checkout badges sit on 0–1 currently-active issuer with 159 members waiting between them — Markforged 3D Printer 31 waiting against 5 qualified and 1 active, Tufting Gun 26/5/1, and Blind Stitch Hemmer 24 waiting, 2 qualified, none active. So what staff hit was not an obscure corner; it was the third most under-served tool on the site. Vacuum Forming (7 waiting) is the other with no coverage at all. The intended shape is: read the demand, call whoever is qualified on that tool, and ask them to come back in — most likely as an instructor, if they would run a class, rather than as a facilitator. drush appointment-facilitator:badge-demand --understaffed=1 reports it and prints the call list; it is staged, not yet on live. planned because the report exists and the outreach has not started — nobody runs this on any cadence and no owner is named. Scored A2/I3 deliberately at the low end of the honest range: the drush report is real tooling, but no instance of this process has ever run, and the cost of failure is members waiting indefinitely on tools rather than revenue or safety. Related trap recorded on the same day: do not "fix" an uncovered badge by relaxing the facilitator role filter on views.view.facilitator_schedules — approval separately requires the approve badge requests permission, which most non-role issuers lack, so they would become bookable but unable to grant the badge. |
| Facilitator appointment feedback | 4 | 3 | 2 | changing | 2026-09-10 Live audit: the booking email links to feedback and a separate next-day email is running; 92/92 noncanceled timerange appointments in the August 11–September 9 cohort have application send-success markers, with 14 written responses (not proof of inbox delivery or email-attributed responses). JR relayed Kate’s request for better timing, then chose scheduled-start eligibility after Live logs showed roughly hourly cron invocation. Start-based timing with “when you have finished” email wording, suppression after written feedback and bounded retries are staged in appointment_notifications 9e47f72 (focused kernel tests 3/53 pass), superseding the earlier end+30-minute proposal; delivery is usually within about an hour of start rather than exact, and Live still uses next-day timing until the bundle ships. Private Slack reminders remain a separate proposal. |
| Facilitator scheduling | 4 | 3 | 3 | stable | Sets and publishes which facilitators are covering which shifts, driven by the facilitator modules rather than a manual rota.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Facilitator 6-month renewal | 2 | 2 | 3 | changing | It runs, and the data proves the cadence. webform_10645 "Current Facilitator Application Update" shows two clear campaigns a year: Feb 2025 (40), Jul–Aug 2025 (33), Jan 2026 (31), Jul–Aug 2026 (38) — against 54 facilitators, roughly a 60–75 per cent response each cycle. The six-month term is real and is actually being run. What is missing is not the process but somewhere to put the answer: the coordinator profile has no term start or end field (availability, capacity, email, focus, hours, on-request, requirements, scheduled hours — and nothing else), which is the concrete reason renewal cannot be reminded, rostered or offboarded automatically. Two dead forms should be archived: webform_1680 "Current Facilitator Reapplication", last used Feb 2019, and webform_8242 "Facilitator Evaluation", last used Aug 2023. (Verified against the database, 2026-08-14) A track opened 08-11 now builds exactly the missing piece — term fields, renewal reminders, onboarding and offboarding — which is why this row reads changing rather than stable. Refined 2026-08-20 by the cycle review, and it makes the missing piece smaller than stated above. The claim that there is nowhere to put a term stands — there is no term field — but the recurrence already carries most of one: field_coordinator_hours is a Smart Date field, all 70 facilitators have a weekly rule (7,744 instance rows, 72 rules, 6 rows with no rule at all), and the rule records a term start for everyone. A term end exists for 13 of 72 — UNTIL=<date> on 12, COUNT=99 on one — while 59 (82%) are unlimited, running forward with no end at all, which is the honest reason renewal cannot be rostered automatically today. So the Phase 0 question is narrower than "add term fields": require an UNTIL when hours are entered and the recurrence becomes authoritative, or add separate fields and accept two sources of truth for data that already drives live scheduling. Do not add fields before that is decided with Kate. |
| On-request badger matching | 3 | 2 | 2 | stable | When no scheduled checkout slot suits a member, they request one and are matched to an available badger. Live since August 2026.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Youth & school custodial partnerships | 2 | 3 | 5 | stable | Materially lower risk than v3 assumed. Minors attend as guests of their own guardians, or through school partnerships where the school's instructor supervises — MakeHaven is not the custodian. Youth safety policies are published. Standards module B still applies, but the screening burden largely sits with partners |
| Peer benchmarking (annual) | 1 | 1 | 2 | planned | An annual comparison of MakeHaven against similar makerspaces — membership, pricing, programming — to test our own numbers against the field. Nothing built.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Public credential transcript (Open Badges) | 1 | 1 | 2 | planned | An opt-in shareable transcript of a member's badges and certifications, mapped to the Open Badges standard so credentials mean something outside the building. On the website master roadmap; groundwork shipped as the member public profile MVP.Description inferred 2026-08-16 — not confirmed by anyone who runs it |
New group in v2. Four custom modules and a dashboard section exist; the operating processes around them mostly do not.
| Process | Auto | Doc | Impact | State | Notes |
|---|---|---|---|---|---|
| Entrepreneur goal capture at signup | 4 | 2 | 3 | stable | field_member_goal — inventor / entrepreneur / seller. 46% of new members identify this way |
| Entrepreneur dashboard & nudges | 4 | 3 | 2 | watch | makerspace_entrepreneur_dashboard. Member half was invisible (soft-launch block placed in the admin theme); fixed live 08-09 |
| Entrepreneurship AI support assistant | 4 | 3 | 2 | stable | Early, experimental, explicitly non-critical and optional per JR. Worth watching against the tool-chatbot precedent rather than investing further yet |
| Nexus platform bridge | 3 | 2 | 2 | stable | Experimental and used at a very light level, with hopes for future development. Members ticking entrepreneur goals should be invited into Nexus, but that path is underdeveloped and underused |
| Incubator workspace intake & graduation | 2 | 2 | 3 | stable | Intake corrected from undefined: applicants apply and the director decides, with live availability published. Graduation remains undefined — nothing defines when a venture should move on The published-availability half was misinforming applicants until it was fixed live 2026-08-28 — see Workspace rental agreement for the defects and the staged fix; the one that lands here is that enquiries arrived with no indication which unit they were about, so the director had nothing to route on.RaisedJR, 2026-08-28 — /workspace showed two conflicting floorplans and every Inquire button led to the same unattributed form |
| Entrepreneurship milestone tracking | 1 | 1 | 3 | planned | Not started. Signup goal data is used for grant reporting and occasional outreach filtering, but no milestone tracking exists. |
| Mentor & advisor matching | 1 | 1 | 3 | planned | Tried and abandoned: "it just did not work well, we would need to do redesign from ground up." A prior attempt exists as evidence, so any restart should begin from why it failed. |
| Entrepreneurship events programming | 2 | 1 | 3 | optimizable | 23 participants against a 300 goal. The gap is 13× |
| Cohort programs (Ecolab-style) | 1 | 1 | 3 | planned | Running entrepreneurship support as fixed-intake cohorts rather than continuous enrolment, on the model of the Ecolab program. Nothing built.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| "Made at MakeHaven" marketplace | 1 | 1 | 2 | stable | Runs at a very light level: a Slack channel where members share design files. Nothing resembling the retail or pop-up presence in the strategic plan. |
| Ecosystem partner data alignment | 1 | 1 | 2 | planned | Actively being worked on — shared data standards with ClimateHaven, Collab and the Community Foundation. |
| Opportunity relay (commissions, gigs & job posts) | 2 | 2 | 2 | changing | Outside people call or email wanting something made, or share a job opening; staff relay it to members by hand-pasting into Slack #jobs. An intake form has existed for four years with nothing downstream of it. A track opened 08-13 closes the loop — intake → light review → automatic post → auto-expiry — deliberately leaving member-to-member posts alone.Description inferred 2026-08-16 — not confirmed by anyone who runs it |
| Venture business profiles & support logging | 1 | 1 | 2 | idea | Business profiles for incubated ventures plus a CiviCRM workflow logging every support touch, so grant applications draw on recorded data rather than recollection. Roadmap backlog; would carry the milestone tracking the dashboard had to retire.Description inferred 2026-08-16 — not confirmed by anyone who runs it |
| Process | Auto | Doc | Impact | State | Notes |
|---|---|---|---|---|---|
| Tool acquisition & commissioning | 2 | 2 | 3 | optimizable | Budget first, then the member wishlist, then consultation with area experts; final call sits with the shop manager and executive director |
| Tool downtime & repair tracking | 3 | 2 | 4 | watch | Major correction — v2 and v3 called this a failed attempt; it is live. asset_log_entry holds 117 records and members report faults that feed the KPIs, with a tool-quality report checking configuration. The earlier "did not get traction" note was historical. New concern from the data: logging has fallen off a cliff — roughly 29/month across April–June, then 5 in July and 3 so far in August. Either faults dropped or reporting did, and those need different responses |
| Preventive maintenance schedule | 2 | 2 | 4 | watch | The shop manager keeps a spreadsheet of recurring tasks for the shop tech. Intent is to formalise it into /tasks; extending it to members is gated on first building a way to certify who is competent to repair what. Two changes shipped live 2026-09-09. A repeating series decided whether to generate its next occurrence from field_task_status rather than from the task_completed flag that actually says the job is done — so a series left open/in_progress produced duplicates and one left incomplete after completion stalled forever. Both directions now follow completion, with kernel tests either way. Members also gained /tasks/request, a members-only form for proposing a maintenance job on a tool or area — the first member-facing entry into this process. Watching for: exactly one open occurrence per recurring series (the bug cut both ways, so look for duplicates and stalls), and whether member-proposed tasks actually get triaged rather than becoming a new silent queue. The spreadsheet → /tasks migration is still the open intent; this release improved the machinery, not the migration |
| Equipment depreciation & replacement planning | 2 | 2 | 3 | optimizable | Currently driven by the CPA, not by us. Once a year they ask what was acquired over $1,000 and what was disposed of. Additions are recorded in the site's equipment inventory. JR wants this connected to actual asset lifetimes so replacement is forecast rather than discovered. |
| Shop budget adherence | 2 | 2 | 3 | stable | A shop budget line exists; the shop manager works within it and the executive director and finance committee monitor. |
| Consumable restock | 3 | 2 | 2 | stable | Store material pages let members flag an item as out; QR codes in the space feed a webform to the operations manager; store inventory has reorder thresholds. Remaining gap: the free supplies out in the space have no system at all |
| Physical inventory count | 4 | 2 | 3 | stable | Used every month; JR reports staff like it and consider it well developed |
| Equipment & area reservation | 4 | 3 | 3 | watch | Added 2026-08-18 by a sweep of the custom modules — the registry had no row for it, and by volume it is one of the most-used member-facing systems we run. Members book a machine or a whole area for a window of time; the booking checks conflicts up and down the asset hierarchy (holding "Laser Area" warns about an existing booking on "Laser 1", and booking "Laser 1" is refused while the area is held), enforces a per-asset maximum duration and minimum advance window, and lets staff override an ancestor conflict rather than be blocked by it. 2,893 reservations to date, 166 in the last 30 days from 64 distinct members; across 90 days 424 confirmed against 61 cancelled (13% cancelled). The gap is documentation, not function: the hierarchy rules and the time limits live only in the module and in field values on each asset node, so a member refused a slot is not told which parent booking refused it, and nothing written says who may reserve what, for how long, or what happens to a no-show. Badge authorization staged 2026-08-20 (e9a6721, deploy pending): until now the booking checked role access but never badges, so a member holding only a Door badge could self-book the $70k water jet — 6 of 226 personal reservations on badge-required tools since launch were made by members without the badge. The gate refuses those, exempting staff and the supervised checkout itself; watch for legitimate bookings being blocked, and soften via badge_gate_mode rather than reverting Badge gate SHIPPED LIVE 2026-08-21 with badge_gate_mode: block. The same deploy's makerspace_reservations_update_9011 repaired 117 of 117 placeholder reservation titles (the %AutoEntityLabel% rows staff were seeing), leaving zero on live. Watching for legitimate bookings being refused — soften via badge_gate_mode, do not revert. |
| Member printing (SavaPage) | 4 | 3 | 2 | stable | Added 2026-08-18 by the module sweep. Members print through SavaPage on an on-site print server: clicking "Open Printing" provisions a SavaPage account just-in-time, mints a one-time SSO token through a bridge service reached over a Cloudflare tunnel, and hands the member into the print queue with no second password. Balances are topped up at /savapage/topup through a Stripe-backed page on the bridge. 31 launches by 11 distinct members across the nine days of retained log, every one successful, no errors. Two things want a decision rather than an assumption. The membership gate the module was built with is switched off on live at both levers — require_active_membership is false and access savapage printing is granted to authenticated rather than to member — so the role, pause and payment-failed checks never run and any registered account can open printing; the practical control is the SavaPage balance, which starts at $0. And the top-up money moves through the print server's own Stripe rather than the site's, so it sits outside the Stripe → Xero reconciliation this registry already tracks |
| 24/7 self-access operation | 4 | 3 | 2 | stable | Reframed — there is no opening or closing. The space is 24/7 and unstaffed, with RFID access and the rules carried by the membership agreement. Standards S017 still wants explicit lone-work and after-hours protocols, which is a narrower ask than "document open and close" |
| Cleaning & shop upkeep | 2 | 3 | 2 | stable | Contracted to a member under a signed contract |
| Building maintenance & landlord liaison | 2 | 2 | 4 | stable | Better than v4 implied. The landlord, Steven Bernblum, is himself a MakeHaven member; contact is by direct phone or text, by email, or through the ManageBuilding portal. Shop manager leads and the executive director escalates. The friction is responsiveness — a request typically needs a text and a reminder or two — not a missing channel. |
| Safety program review & drills | 2 | 3 | 5 | stable | The Safety Program Summary plus tool hazard levels are the core, with the rest embedded in the badging system and the membership agreement. Emergency drills remain unaddressed — Standards S011/S019 want them plus a trend review |
| Incident & near-miss reporting | 2 | 3 | 5 | stable | Members and staff report incidents and near misses through an online form. The Safety & Accessibility committee reviews submissions quarterly, and reports reach a standing list of staff and volunteers. Real gaps are narrower — paper forms are re-keyed into the online form by hand, and the reporter is never told what happened |
| Accessibility / ADA audit | 2 | 2 | 4 | stable | A second annual walk-through by the same committee covers accessibility. JR: the audit happens, but there is no defined review-and-improvement loop after it |
| Insurance / lease / compliance renewals | 2 | 3 | 5 | stable | Now fully documented — the single biggest bus-factor win of this exercise. Nine recurring items across insurance (General Liability + Umbrella in February, D&O in April, Workers' Comp in October, all through Wellstone), the 770 Chapel St lease, and compliance filings (CT annual report in March, CPA review April–June, Form 990 extended to November, CT charitable solicitation, board COI disclosures in July). Every one is still triggered by an inbound email rather than a calendar, which is how the 990 was late in 2025. Written up in full at docs/ops/RENEWAL_CALENDAR.md. |
| Sustainable operations practices | 1 | 1 | 2 | planned | What the organisation itself does about waste, energy and materials in the shops, as distinct from what members are asked to do. Nothing built.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Chemical inventory & SDS access | 2 | 3 | 5 | stable | Dedicated flammables cabinets, a printed SDS binder for commonly-used chemicals, a published air-quality usage policy, and air monitors verifying conditions. JR notes the binder is due a review |
| Lockout / tagout of unsafe equipment | 3 | 2 | 5 | stable | Anyone may place a warning sign; staff apply physical cord locks; every tool page carries a report-issue button. Remote lockout is designed but blocked on tool access control being fixed |
| Routine shop inspection walk-through | 2 | 2 | 4 | stable | The Safety & Accessibility committee walks the shop once a year for general safety |
| Hazardous & material waste disposal | 2 | 3 | 4 | stable | It is a stated policy, not an oversight. The membership agreement places responsibility on members to remove what they bring in. Standards S020 still expects an organisation-side procedure for anything generated by our own operations. |
| Equipment satisfaction micro-feedback | 1 | 1 | 2 | idea | Lightweight prompts — a QR code on the machine, a one-click "how was this tool?" after a badge-logged use — feeding the equipment-satisfaction number now collected only by occasional surveys. Three variants sketched in the website TODO; none started.Description inferred 2026-08-16 — not confirmed by anyone who runs it |
| Process | Auto | Doc | Impact | State | Notes |
|---|---|---|---|---|---|
| Door access control (building entry) | 4 | 3 | 5 | stable | JR confirmed on 2026-09-10 that MakeHaven’s home-built building-entry system works. The previous broken classification combined normal operation with historical availability concerns and an earlier instructor eligibility issue; it is not a current failure report. Offline resilience remains a separate improvement question, and this confirmation does not establish outage behavior. |
| Tool access control (interlocks) | 2 | 2 | 5 | broken | The most consequential finding in the registry. The microcontroller locks up after a couple of days and the bug has not been found. The workaround — trusting members to use only tools they have been signed off on — has been the operating reality for almost two years. So tool authorisation is currently a social control, not a technical one, and the remote-lockout capability that would take an unsafe tool out of service depends on the same system. |
| Access-control hardware operations | 4 | 3 | 5 | stable | Home-built ESP32 plus Home Assistant. Vincent, Corey and Lior hold the knowledge, and a documentation site is linked from the GitHub repository in the MakeHaven org account — GitHub has been the key recovery tool more than once. UniFi Access is an add-on to this, not a parallel system. |
| Event visitor passes | 4 | 3 | 2 | stable | QR / PIN for registrants, via the UniFi add-on |
| Access request approval | 3 | 2 | 3 | stable | A member asks for access to a tool or area they are not yet cleared for, and staff approve or decline it.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Guest & waiver handling | 3 | 2 | 5 | stable | A guest signs the waiver, it is stored in the system, and they are offered the option to join the email list. JR believes this works. That is the whole process — there is no follow-up sequence, which is the likeliest explanation for 2.4% conversion against a 5% goal on 218 waivers a year. |
| Member T&C re-acceptance gate | 4 | 3 | 4 | watch | Discovered 2026-08-15 by measuring it — nobody had a row for it. When the waiver is revised, every member is locked out of the whole site at next login until they re-accept. It runs unattended but tells nobody when it fails, so failure surfaces only as a member complaining to staff. Over five days of live logs 21 of 35 clients that reached the gate never submitted it, and 615 of 862 active members are still behind the v7 gate. Three prior fixes all patched the session lock; the lock was never the fault — the accept page gave no instruction on arrival, put the first actionable control ~3 screens down, and made Confirm a silent no-op when any of five required boxes was unticked. Fix shipped live 2026-08-15 (makerspace_legal_gate, plus the accept page moved to the scroll-box style): an instruction callout on arrival, a jump link to the boxes, and native validation replaced by an in-page error naming the box still unticked. Watching: whether the 187 accounts still behind the gate now clear it, and whether staff stop hearing about it. It still tells nobody when it fails, which is why A stays 4 rather than 5. Mechanics re-verified 2026-08-24 by the member-onboarding flow check, which now photographs this branch: a fresh account that has never accepted lands on the gate, gets the "Please accept the updated terms to continue" callout on arrival, a working "Go to the boxes" jump, and — pressing Confirm with nothing ticked — the real message "You must accept the Terms and Conditions before continuing" instead of the old silent no-op. That confirms the repair, not the outcome: whether the still-gated accounts actually clear it, and whether staff stop hearing about it, is what this watch is still open on. D scored low deliberately: a README now describes it, but nobody has yet worked from it unaidedRaisedKate Cebik, 2026-08-15 — members repeatedly stuck and blocked from doing anything |
| Tool issue reporting | 4 | 2 | 3 | stable | Webform to Slack |
| Tool status communication | 4 | 3 | 3 | stable | Tells members when a tool goes out of service and when it returns; status changes post to Slack automatically.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| API endpoint security | 3 | 3 | 5 | broken | Open critical finding (SEC-001): access_control_api_logger's /api/v0/ routes are all _access: 'TRUE' with no caller auth, exposing the door grant-or-deny decision, member name/UUID/status/photo, and the full badge catalog to anyone on the internet. Re-verified 2026-08-20: an anonymous GET /api/v0/permissions/list still returns HTTP 200 with the whole catalog. The real fix is blocked on hardware — the ESP32/Home-Assistant readers are live consumers and must send the new auth before we enforce it. ~~The unblocking task is small and nobody is on it: find the readers' egress IP~~ DONE 2026-08-24 — the egress IP is 32.218.105.26 (SNET, the building's ISP), identified from three days of live nginx access logs exactly as predicted: the module logs no caller IP, so it had to come from Pantheon's logs. /api/v0/ can now be restricted at the Pantheon edge with no firmware change, which closes the internet exposure that makes this CRITICAL. Caveat to write into the rule: that address is the whole building's NAT — readers, kiosks, staff laptops and guest wifi all egress through it — so the allowlist closes the internet and not the LAN. It is an interim mitigation and does not retire the HMAC/shared-secret work, and it must be re-verified after any ISP or router change, since a dynamic reassignment would break every door in the building. Full finding and remediation tracks in the security ledger |
Grouped together because the plan treats them as one thing — Program and Product Line Monitoring: "evaluate major program areas (store, lending library, membership, courses, storage, and workspaces) for cost recovery and mission alignment."
| Process | Auto | Doc | Impact | State | Notes |
|---|---|---|---|---|---|
| Borrower onboarding | 4 | 3 | 2 | stable | Role assigned on webform submission |
| Loan checkout | 4 | 3 | 3 | stable | Module carries its own docs |
| Loan return | 4 | 3 | 3 | stable | Closing out a lending-library loan — the item is checked back in and the member's hold released.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Overdue late fee | 4 | 3 | 4 | watch | The double-charge fix shipped live 2026-08-14, but the refunds it was supposed to enable are silently failing. Two refund attempts on 2026-08-15 (transactions 4851, 4857) were rejected by Stripe: the rk_live_ restricted key in use lacks charge_write, so the site logged an attempt and no money moved. Resolved by 2026-08-18 and verified against Stripe 2026-08-20: the key was granted charge_write, and all three refunds then went through the site's own form — transaction 4851 ($30.00) and 4857 ($5.00) at 23:07/23:08 UTC on 08-18, plus 4866 ($5.00) at 23:14, each matched by a succeeded refund object in Stripe. Nobody is out of pocket, and there has been no refund failure since. The reason this row is watch and not stable is the pattern rather than the incident: this was the second scope missing from that key discovered by a production failure — the store tab pilot hit the same key missing Customers:Write — and both times a member hit the hole before we did. The key's scopes still want auditing against every Stripe write path in one pass rather than being widened one outage at a time; until that happens, assume a third scope is missing somewhere nobody has exercised yet. Found by the 2026-08-17 weekly pulse. (A 2026-08-20 re-check first recorded this as still broken by searching watchdog for errors and finding no newer ones; searching for successes instead showed the refunds had gone through three days earlier. Absence of new failures is not evidence of a fix.) 2026-09-10: counting corrected. Loans due today were being reported as overdue — the manager showed 3 against analytics' 1. Overdue is now a calendar-day comparison with due-today shown separately, and page and CSV date ranges agree. A reporting fix only; the charging path is untouched. |
| Damage deposit | 3 | 2 | 3 | stable | Taking and releasing a deposit against damage to a borrowed item. Runs, but nothing is written down and no module implements it.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Battery tracking | 3 | 2 | 2 | stable | Knowing which tool batteries exist, where they are, and whether they still hold a charge.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Missing / lost item handling | 3 | 1 | 3 | watch | Guard shipped and verified present on live 2026-08-17. Reporting an item missing from the issue form is now blocked when it is on loan to someone else, and points staff at the admin Mark Missing flow, which keeps the loan attached instead of silently closing it and orphaning the battery. What remains is the detection side: the borrowed_or_missing_without_borrower integrity check conflates "borrowed without a borrower" (a real fault) with "missing without a borrower" (usually expected), and reads as failing because of a stale write-off backlog nobody has triaged — so the one instrument pointed at this process cannot currently tell a fault from housekeeping |
| Item repair & retirement | 2 | 2 | 2 | stable | A broken library item is removed from circulation. JR's intent is for lending fees to eventually fund replacement, which they do not today. |
| Librarian role administration | ? | 1 | 2 | optimizable | Granting a new librarian has been open for weeks |
| Lending budget review | 3 | 2 | 2 | stable | Periodic check of what the lending library is spending on replacements, additions and losses against what it has.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Storage assignment | 3 | 2 | 3 | optimizable | Assigning member storage space and working the waiting list when it is full, under the published storage policy.Description inferred ), never human-verified ( — not confirmed by anyone who runs it |
| Storage billing | 3 | 2 | 4 | watch | Worse than v4 recorded. A re-audit against live Stripe on 2026-08-14 found 24 active assignments, roughly $565/month uncollected — not the 7 and ~$225 previously logged. Seven were never linked and seventeen point at dead, cancelled or expired subscriptions. This is ongoing rather than legacy (two failed this week) and the root cause is that nothing feeds subscription state back: there is no webhook or cron reconciliation and the status field was never created. No member has been over-billed. 2026-09-10: value reconciliation shipped. Billed value was counting $132/month of list price from six occupied-but-unassigned units, and three orphaned assignments had no route to reach them. Assignment and list values now reconcile, exceptions are prioritised and searchable, and invoiced is distinguished from complimentary. Reporting only — no billing action was taken; do NOT treat a surfaced exception as a safely repairable subscription without investigating it. |
| Store purchase | 3 | 2 | 3 | watch | 2026-08-26: the guest-PayPal raw-markup fix shipped live along with the missing restock button and the dead supplier= param. Deliberately not yet reported to the member who found it: verified anonymously only, and ledger items 44325 / 44550 / 44551 stay open until a logged-in look at /store confirms it. Prior state below. Member plywood complaint 08-17 confirmed /store rendered only classic-PayPal buttons (now behind a PayPal auth wall; sales 168/mo Apr → 28 by mid-Aug) with zero links to the tab/Stripe checkout. Fix shipped live 2026-08-20 (config-only): every buy button on /store, material pages and related-supplies blocks now routes to /store/checkout-item/{nid} with a guest-PayPal fallback. Verified on live 2026-08-20 — the staged config is deployed (the local-vs-live config diff no longer contains it). One day of data, too early to call: in-product transactions ran 43 in July and 57 so far in August, and the day after the deploy carried 6 against a recent daily median of 2. Re-measure the PayPal-vs-Stripe split and tab uptake in the week to 2026-08-27 before moving this to stable. 2026-08-24 weekly pulse — the fix itself regressed, and a member found it before we did. The deployed config builds the guest link as href="…webscr?{{ nothing_2 }}", and nothing_2 composes its query string with item_name={{ title }} — but title on this view is the linked title field, so its rendered value is a whole <a href="…">Item Name</a>. The inner quote closes the href early: the guest link points at a truncated, unusable PayPal URL, and the remainder of the query string renders as visible raw text under every Purchase button on /store, on material pages, and in Related Provided Supplies (four display sites). The member-facing Purchase button to /store/checkout-item/{nid} is unaffected and still works, so this is broken for the guest path and ugly for everyone, not a total outage. It never showed before because nothing_2 had only ever been consumed through Views' path: setting, which URL-encodes it. Reported as feedback sid 20328 on 2026-08-21, the day of the deploy; root-caused in the 2026-08-24 pulse; entry and three fix options in DEPLOY_TODO, public ledger #44550. The measurement above is now confounded — any PayPal-vs-Stripe split read in the week to 08-27 is a read of a broken guest link, so re-measure only after the fix. Two related store items came in from the same reporter the same day: material pages reached from /store carry no Request Restock button (ledger #44551), and restock-request emails intermittently omit the supplier and material link buttons — a re-report of the same complaint from 2026-05-11 that was never fixed (ledger #44552). |
| Store member tab collection | 4 | 3 | 3 | watch | Measured 2026-08-26: the collection half is healthy, the adding half is not. $850.29 collected all-time, 53 members opted in, 0 blocked, every opted-in member carries a Stripe customer ID, and August alone settled 14 invoices — so the pilot works. Three defects underneath it, all staged 2026-08-26 (makerspace_material_store 6368013), none shipped yet. (1) Repeat adds. Five members in August each created three to seven identical tab lines within two minutes — one added the same $5 access card seven times — and every extra was reversed by hand; they are most of the $257 “removed” that month. The cause was the confirmation, not the members: the modal closed on success and redirected to /store, a different page from the one they were reading, where an unrequested status message was the only evidence anything had happened. Nothing answered “did that work?”, so they clicked again. Now answered before (what is already on the tab), during (progress message, repeat clicks swallowed) and after (the modal is replaced in place with a receipt carrying the new tab balance and a direct undo), plus a confirmation tick on an exact same-material-same-quantity repeat inside 10 minutes. (2) The opt-in card check failed open on every error, so a member whose stored Stripe customer no longer exists could opt in and accumulate an uncollectable tab (uid 3230, 08-24, six failed charges over two days). Permanent rejections now refuse; transient ones still pass. (3) The Stripe idempotency key did not cover the line set, so a second manual charge in the same month — what staff do after removing duplicate lines — was rejected outright by Stripe (uid 3891, 08-23, twice). The old Customers:Write note is superseded: opt-in linkage is no longer the live problem, though it belongs in the one-pass key-scope audit the Overdue late fee row asks for. Two members still need a human, not a deploy: Ellen Carson has $1.50 on an open Stripe invoice since 08-14 that escalated to staff on 08-16 and has sat untouched since (the retry ladder worked; the human end of the loop did not), and Dani Ramirez has $38.00 pending against a freshly-rewritten customer ID nobody has confirmed carries a card — check before the 08-31 month-end run. SHIPPED LIVE 2026-08-28 (release 2026-08-27-workspace-photos-digest-store-lapsed-audit). Rehearsed on test through the real validateForm/submitAddToTab rather than units: the first add created a line, an exact repeat at the same quantity was refused with a confirmation tick and created nothing, a different quantity passed untouched, and the confirmed repeat went through; the receipt rendered with line total, new tab balance and a working undo link. Moves changing → watch. Watching: whether August's repeat-add pattern (five members, three-to-seven identical lines each, most of the $257 reversed that month) actually stops, and whether the 10-minute duplicate_window_minutes default proves right — it is unset, so it lives in code until someone sets it at /admin/config/makerspace/store. Announced as changelog #44798. Not fixed by this and still open: Ellen Carson (uid 1381) $1.50 stranded on an open Stripe invoice, which needs a human. |
| Store inventory restock & reorder | 3 | 2 | 2 | stable | Some materials priced at $0.01/unit — any unit cap becomes a silent spend limit |
| Line-of-business profitability review | 2 | 2 | 4 | stable | The finance committee has actually done this, breaking results down by line of business. It is laborious, and work is ongoing to make Xero produce it more directly. |
| Process | Auto | Doc | Impact | State | Notes |
|---|---|---|---|---|---|
| Join / signup flow | 3 | 3 | 4 | watch | Current2026-09-10: JR reports improved signup; the original email-handoff defect is verified fixed in the recorded Live audit. Full payment-recovery acceptance remains open.Next actionNext onboarding review session: address the paid-applicant recovery finding in the existing member-onboarding work record; do not direct an already-paid applicant to pay again.Next reviewAt the next onboarding review and after the recovery fix is ready for acceptance.AcceptanceVerify payment-to-account linking and safe recovery when account creation fails. JR’s improvement report does not claim those remaining checks passed. |
| Email validation at signup | 4 | 3 | 3 | watch | Fixed and live 2026-08-18 (makerspace_email_guard). Scope had been recorded as unmeasured; it was not small. Across the CRM's 1,393 distinct email domains, 114 resolve to nothing at all (no MX, no A, no AAAA), covering 173 contacts, 62 with no Drupal account — people who got a contact record, have heard nothing from us since, and read as no-shows rather than broken signups — plus 120 Drupal accounts on a dead domain. Dead domains are now blocked at the register form, every webform, and CiviCRM's own event registration, which is where most of these actually came from and which no Drupal form alter can see. Live typosquats (gamil.com, oulook.com) are only suggested, never blocked, because gmx.com, aim.com and qq.com are real domains members use and sit just as close to a big provider. Fails open if DNS is unreachable, so an outage cannot stop signups. Watching two things: that nobody reports a legitimate address being refused (the tell is a "skipped the deliverability check" warning in watchdog — none at deploy), and that the weekly cron re-scan keeps the report honest. A4 not A5: it runs unattended and logs, but nothing tells a human unprompted. The backward-looking cleanup of the 62 is a separate open task and needs a person, not code. Watch check 2026-08-20 (two days in, holding but too early to close): across the 12 days watchdog retains, the module has logged exactly one line — the weekly re-scan notice on 2026-08-18 — and zero "skipped the deliverability check" warnings, which is the stated tell for over-blocking. The re-scan has only fired once, so its cadence is asserted rather than demonstrated; re-check after the next two Mondays before moving this to stable |
| Payment setup | 4 | 3 | 4 | stable | Chargebee portal |
| Membership status sync | 4 | 3 | 4 | watch | On 2026-08-20 by measuring it. The webhook matches a member by field_user_chargebee_id and drops the plan write when no account carries that ID yet — no queue, no retry. In one 12-day log window 14 of 16 "No user found for Chargebee ID" warnings resolved to accounts that exist now, most created the same day. The miss is permanent because only subscription_created/_updated/_reactivated write the plan; renewals and payments do not, so a member who simply renews is never repaired. It surfaced as members reading "Unassigned" on the membership snapshot: 9 active members, 8 of them genuinely paying, about $340/month absent from MRR because the finance queries treat "no plan row" as "not a Chargebee member". The 8 were repaired by hand on live 2026-08-20 (2 were not linked to Chargebee at all and had their customer ID set; one owned two customer records and was linked to the live one), taking Unassigned 9 → 2. Stays broken because the mechanism is unchanged on live: the fix — a daily PlanReconciler cron sweep that re-derives from Chargebee, plus chargebee:reconcile-plans — is staged, deploy pending (chargebee_fetch_data 528e9a5, confirmed still on neither test nor live as of 2026-08-20). Move to watch after it deploys and confirm the sweep reports 0 repaired. The two members still Unassigned have no Chargebee record at all and need a person, not code Reconciler SHIPPED LIVE 2026-08-21 (chargebee_fetch_data 528e9a5). The daily sweep now runs in cron; the first live run reports 3 checked, 0 repaired, which is the expected result — the backlog had been cleared by hand on 08-20. The mechanism is no longer the unchanged one described above, so this moves off broken. Watching the first live cron day for Chargebee plan sweep: in watchdog. The remaining two members (jwthomp14 uid 5490, mjosepk12 uid 5527) have no Chargebee record at all and still need a person, not code. |
| Dunning / payment recovery | 3 | 3 | 4 | watch | Chasing failed and lapsed membership payments before they turn into cancellations. A first phase went live in April 2026. 2026-08-20: staff review (Kate/Christina) found the Intervention Performance page untrustworthy, and they were right — the outreach log auto-records wins (member pays → back-attributed payment_updated row) but never losses: a member whose subscription cancels mid-recovery drops out of the snapshots with no row, so Confirmed Cancel read 0 for everyone since 2026-04-20 (28 real departures unrecorded; 17 had Christina's outreach within 30 days). Fix staged (makerspace_member_success 79c45cf): departures now auto-log a back-attributed confirmed_cancel, an ms:backfill-cancellations command repairs the gap, the resolution-rate card gained a names drill-down, and the Full Contact Log export now actually exports the log. Move to watch after deploy + backfill; verify Kate/Christina accept the corrected numbers SHIPPED LIVE 2026-08-21 and the backfill has run. ms:backfill-cancellations processed 27 departures on live (8 skipped as still-active members); the same command had been rehearsed on test with the same 27. Departures now auto-log a back-attributed confirmed_cancel, so Confirmed Cancel should stop reading 0. Still open and the reason this is watch not stable: Kate and Christina have not yet been told what the corrected numbers mean — resolution credit is back-attributed within 30 days, and "self-recovery" rows are members who paid with no human contact. Their reports were ledger #44334.Description inferred 2026-08-20 — not confirmed by anyone who runs it |
| Cancellation & offboarding | 4 | 3 | 4 | broken | Member cancels in Chargebee; the sync module removes the member role, which removes door access; the cancellation reason is captured and synced to Drupal/CiviCRM. A failed payment routes to dunning and then the member-success queue instead. (JR, round 3 — recorded stable 2026-08-14) 2026-08-25 — the revocation half does not reliably happen, and the rest of the flow works so well that it hid this. Kate asked why a cancelled member's box was still ticked (Albert Pimpinella, uid 5696). The webhook had fired and recorded the reason and end date correctly; it removed no role, because it only ever looked for member and he was still member_pending_approval — he had signed up the day before. The removal logged only on success, so the miss left no trace; the approval a day later then granted him membership, and with an active door badge and no pause flags he passed every access gate. He was still badging in two weeks later with Chargebee reporting mrr: 0. Nothing disagreed afterwards either: no job asked whether everyone holding the role was actually paying. Asking Chargebee directly on 2026-08-25 found 12 accounts holding a membership role with no live subscription, each individually confirmed cancelled; 8 of them can currently open the door. Counting from our own fields found only 5 of the 12 — three have no membership end date recorded at all, four hold only member_pending_approval so a roles = member query never sees them, and one carries a future end date. Our own data could not have produced the list. Impact raised 3 → 4: this is door access to an unstaffed 24/7 space plus uncollected dues, not internal friction. Fix SHIPPED LIVE 2026-08-26 (cancellation strips onboarding roles too, a warning when a lapsed account is granted the role, and a nightly audit against Chargebee). 2026-08-27 — detection is now proven and the remaining gap is a decision, not code. The audit's first nightly run on live (08-26 21:34) checked 864 members and named the same 12, unprompted. Kate ruled on the list the next day: four are genuinely lapsed and still hold the role — uids 2962, 5186, 5225, 5696, every one of them with an active door badge — and the other eight were intentional or have since been remedied. A read-only run on live that day returned 858 checked, exactly those 4, all status=cancelled in Chargebee — no false positives. The operating rule is JR's: Chargebee is the authority — an active plan means access, a deactivated one means none — and that is already what the audit implements, since it counts active, in_trial, future, non_renewing and paused as live. A plan-name carve-out exempting invoiced accounts was written and reverted the same day: it assumed those meant membership with no subscription behind it, but uid 5576 is active and uid 719 non_renewing, so neither was ever at risk, and matching on plan name would have kept an invoiced member in forever once their subscription was cancelled. Two of Kate's four (uids 2962 and 5186, cancelled in April) have no end date and no cancellation reason recorded at all — for those the webhook never wrote anything, so the role-stripping fix would not have caught them either; only an audit that asks Chargebee directly finds that shape. Two integrity guards staged 2026-08-27 for the day it is allowed to act: a truncated Chargebee fetch aborts instead of reading unfetched pages as nobody-paying, and an implausibly large lapsed count reports without acting. Still broken: four people can open the door today without paying. It closes when JR authorises drush chargebee:audit-lapsed --revoke on live and the auto-revoke flag is flipped |
| Member conduct & discipline | 2 | 3 | 5 | stable | The executive director decides, following a written guide. The member may appeal to the board chair, who either lets the decision stand or takes it to the board to consider overruling. Low volume, clear escalation path; the weak point is that records live in a spreadsheet rather than the CRM. |
| Exit survey & ending-reason capture | 4 | 2 | 4 | stable | Reason is captured at cancellation in Chargebee and flows through to the profile, so it is automatic rather than a survey anyone has to chase |
| Rejoin / recapture campaign | 2 | 1 | 3 | stable | Runs: a contractor does outreach and rejoin buttons exist in the product. What has not happened recently is any larger marketing push to lapsed members. |
| Comped / sliding-scale / sponsored memberships | 4 | 2 | 3 | stable | Applicants self-certify on the join form with an online signature — no approval queue by design. The intake is automated; the policy behind it is not written down. There is an application form and a pricing policy, but nothing states who qualifies, what the sliding scale actually is, or how a scholarship differs from a comp — so the decision rests on whoever is asked. JR, 2026-08-15: no documented process exists for this |
| Stripe customer linkage | 2 | 2 | 3 | optimizable | 656 unlinked; field only written by manual backfills |
| Membership pricing review | 2 | 3 | 4 | stable | Board policy sets an annual inflation-linked default; the executive director holds the authority to apply it |
| Workspace rental agreement | 4 | 3 | 3 | watch | Signing flow live 07-29; retire gate live 08-12; inquiry funnel staged 08-20 (f3c5e50, deploy pending): applicant acknowledgment email, availability+floorplan block on /workspace, recent applications on /admin/workspace — watch the first post-deploy application for both receipt and staff emails Inquiry funnel SHIPPED LIVE 2026-08-21 — applicant acknowledgement email, the availability + floorplan block on /workspace (floorplan v2 overlays the drawn plan rather than the schematic), and recent applications on /admin/workspace. Still watching the first real post-deploy application for both the applicant receipt and the staff email, and Kate has not yet been told her manual acknowledgement step can stop. Photos on the listing have never worked — found 2026-08-27. The card template and ListingBuilder have rendered a workspace photo since the funnel was built, but field_wr_photo was created by an update hook and never exported to the site repo's config — and cim deletes active config missing from sync, so every deploy purged the field, its instance, both display components and the workspace__field_wr_photo table. Live carries schema 9005, proving the hook ran, with no photo field present and all 15 cards falling through to “Photo coming soon”. Fix staged 08-27 (7eb8e65 plus the four config items) so config import creates the field instead of purging it; verified locally by deleting the field, running cim, and round-tripping a real upload through the edit form and onto a card. Marked broken rather than watch because the defect is still on live — it returns to watch when the next bundle ships, and the photos then need a human to actually upload them. Photo fix SHIPPED LIVE 2026-08-28 (release 2026-08-27-workspace-photos-digest-store-lapsed-audit). Verified on live: field_wr_photo, the workspace__field_wr_photo table, the image_image widget and the image@large formatter all present after cim — the deploy that used to purge them now creates them. Returns to watch as this row said it would. What is still not delivered is the photos themselves: all 15 cards still read "Photo coming soon" until staff upload, so no changelog entry was written and none should be until they do. Ledger item 44238 (reported 3× by Kate, 11/18/24 Aug) flipped to shipped and both reporters were emailed. Two more defects on the public page, found by JR 2026-08-28 — the page has been showing prospects contradictory information. The interactive floorplan's base drawing had monthly prices baked into it, from an older plan whose 1st-floor rooms are drawn 174/179/176 sq ft against the entities' real 152/148/151 — so the drawing contradicted the live price chips drawn on top of it, the cards beside it, and a second static copy of the current price-free plan that the node body rendered directly above it. The listing also rendered twice, once from the body's [[WORKSPACE_AVAILABILITY]] token and again from a leftover block placement at the foot of the page. Separately, every one of the six "Inquire About This Space" buttons linked to the bare inquiry form: the applicant got no confirmation that the space they clicked had registered, and staff received applications with no way to tell which unit they were about — which is the half that costs real work, since the whole point of publishing availability is to route the enquiry. Bonus, found while in there: <meta name="description">, og:description and twitter:description on /workspace have been publishing the literal string [[WORKSPACE_AVAILABILITY]] to Google and every social card, because the metatag defaults map all three to [node:summary] and the body had no summary. All four staged 2026-08-28 (workspace_rental c9ebccd, theme ec7a7b3, webform_2093 config, block deletion, and a content port for the node body). Cards now deep-link ?workspace_id=<id>; the form greets the applicant with the space it resolved and presave stamps the readable label so the staff email reads "Space of Interest: 1st Floor — Room 7 (230 sq ft, door)" instead of "Workspace ID: 19". The base drawing is now the price-free 2023 plan with all 14 regions re-traced (1600x664 → 1200x502) and every sq-ft label matching its entity. Deploy-order caveat worth keeping: the duplicate block was the only thing attaching the listing's CSS/JS to the token-rendered copy — renderInIsolation() discards #attached — so deleting the block without the theme bump renders the floorplan as unstyled black polygons. Marked broken rather than watch because all of it is still live; it returns to watch when the next bundle ships. Not fixed, noted: basement Bay #1 (98 sq ft on the drawing) has no workspace entity, so it is inert on the plan and absent from the cards — staff decision, not a code fix. SHIPPED LIVE 2026-08-28 (release 2026-08-28-workspace-page-and-entity-security). Verified on live: one floorplan where there were two, the drawing carries no prices, 3 available spaces render green with a live price chip, 11 rented are hatched and labelled "Rented", the 2 rooms we do not offer are greyed, no JS errors. Inquiry deep links verified for ids 1/6/19 — each prefills and greets with the right space and specs, an invalid id degrades to no notice. <meta name="description"> and og:description are prose again instead of the raw [[WORKSPACE_AVAILABILITY]] token. Ledger 43642 ("Workspace page does not match the spaces we actually have", reported 2026-07-23 via sid 20033, open as planned for five weeks) flipped to shipped and Kate was emailed; changelog #44806 and #44807 published. Watching: whether inquiries now arrive carrying a space — the whole point is that staff can route them, and nobody has submitted a real one through the new path yet. The live submission was deliberately NOT rehearsed on live (it would email three staff addresses plus an applicant receipt for a fake person); it was proven end-to-end on test through the real submitFormValues, where the staff email read "Space of Interest: 1st Floor — Room 7 (230 sq ft, door)" and a tampered value was overwritten. So the first genuine live inquiry is the thing to check. Also still true: basement Bay #1 (98 sq ft) has no workspace entity — JR accepted it as un-rentable for now, and it renders as "not currently offered" rather than as the brightest room on the plan. Cache defect found and fixed the same day (2026-08-28). The drawing was correct on the server and wrong in the reader's browser — see Deploy pipeline for why. Live now serves floorplan-base-2023-layout.png?v=<content hash>; the old URL 404s.RaisedJR, 2026-08-28 — reported the duplicated/price-conflicting floorplan and the unattributable Inquire buttons on /workspace |
| Workspace rental billing | 2 | 2 | 3 | stable | And verified: 14 of 15 workspaces now carry a price — Kate filled them. Many tenants are paying, invoiced through Xero today, with migration to the in-product system intended later. |
| Denied-entry rejoin follow-up | 1 | 1 | 2 | idea | When the door denies someone with no active membership, nothing follows; the sketch is an automatic email/SMS explaining why, with a mobile rejoin link and quiet-hour rules. Would give the rejoin campaign a trigger instead of a list.Description inferred 2026-08-16 — not confirmed by anyone who runs it |
| Process | Auto | Doc | Impact | State | Notes |
|---|---|---|---|---|---|
| Onboarding nudge / stalled-join recovery | 4 | 3 | 3 | watch | Live 08-11, profile-step only. Efficacy unproven — before widening, 0 of 5 nudged progressed. 08-17: widening to the full funnel (video/quiz/schedule) staged, deploy pending — triggered by Glenn Bair stalling at the video step with no automated email; watch post-deploy whether video-stuck members progress |
| Orientation scheduling | 3 | 2 | 3 | stable | Calendly. A webhook outage lost Apr–Jun records; orientations never backfilled |
| First-badge-in-28-days promotion | 3 | 2 | 4 | optimizable | Measured but not managed — JR: "now just measured and we work against it." 67% against an 80% goal. Current experiment: sending a free GEMS course to members who appear stuck.RaisedStaff, 2026-08-14 — part of the badge-earning flow, which staff see as a major driver of retention and a strong candidate for review soon |
| Interest capture + Slack invite | 4 | 2 | 2 | watch | Captures someone who registers interest in joining and invites them into Slack automatically. Live since August 2026 and still being watched.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Member success outreach queue | 3 | 3 | 3 | watch | A worklist of members for staff to reach out to — newly joined, gone quiet, or at risk of leaving. 08-17: Kate proved the recovery side was hiding its targets (4 named members, 7 total); episode-reset policy (new payment failure re-opens files, Kate-confirmed) + Chargebee write-off flag clearing + cron CiviCRM backfill (Christina-work visibility) + template 166/167 repair all STAGED, deploy pending 2026-09-10: reporting corrections shipped live — changing → watch. Outreach reporting dates aligned, first-contact-to-first-positive timing now counts all contacts rather than ignoring earlier unsuccessful ones, and obsolete latest-flags are cleared after the daily snapshot completes. Watching whether the daily run stays clean and whether the corrected timing changes what staff conclude about which interventions work.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| At-risk early-warning detection | 4 | 3 | 4 | stable | Year 1 list marks this "(done, improve?)" — the one strategy already built |
| Retention intervention tracking | 3 | 2 | 3 | stable | A contractor works the at-risk queue using member-success module data. Whether interventions actually change outcomes is charted but not, as far as we can tell, acted on. |
| New-member 3-month survey | 1 | 1 | 3 | planned | Asking members three months in how joining has actually gone, while it is recent enough to act on. Nothing built.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| New-member gatherings & peer intros | 2 | 2 | 2 | stable | Split result. Social events and meetups genuinely happen and are frequent. The mentorship programme fell apart and is dormant — that half is the gap, not the social half |
| Interest-based member groups | 1 | 1 | 2 | stable | Runs loosely through Slack channels and meetups rather than as a supported programme with stipends. |
| Member recognition & awards | 1 | 1 | 2 | changing | In the works, and has been for a long time — the awards concept keeps being picked up and put down. |
| NPS & satisfaction survey | 2 | 2 | 3 | stable | Member surveys carrying NPS have run for years. JR wants the process integrated into the site and automatic rather than periodic and manual — and flags it as something to check back in on |
| Community Wishlist | 4 | 2 | 2 | stable | It exists and runs. Members submit and vote; it also feeds the tool-acquisition process as the step after budget |
| Deferred profile field capture | 3 | 2 | 2 | watch | Re-measured 2026-08-17 and it was three things, not one — and the biggest was not a capture problem at all. Discovery recovered once it moved to the thank-you interest picker (8 of 15 recent profiles, against 0 of the preceding 10). Bio has a capture moment now too (the Slack intro banner) but has still produced nothing. The third part was a lock: emergency contact name, emergency contact phone and preferred phone were all required on profile.main, so 115 of 858 active members (13.4%) could not save any profile edit — not a bio, not a headshot — until they supplied an emergency contact nobody had ever asked them for. That is what dead-ended every "you can fill this in later" path, and it was routinely misreported as "the bio field is required" (bio is not required). Fixed live 2026-08-18: the requirement now applies on the join path only, so new members are still asked while established members get a prompt and a one-purpose form at /membership/emergency-contact instead of a wall. Watching the number: 115 at deploy — if it does not fall, the passive banner is not enough and the answer is an email, not another form tweak. That is the same lesson the discovery fix taught: a deferred field needs an active capture moment or it is not collected |
| Member file bucket | 4 | 3 | 1 | stable | Added 2026-08-18 by the module sweep. A deliberately short-lived file drop at /bucket for moving files between a member's own device and a shop computer: members upload, the list is public, downloading streams the file and marks it used, and cron deletes it — a 48-hour TTL and immediate deletion after first download, 50 MB cap, dangerous extensions blocked. The public listing is intentional and disclosed on the page itself ("Anyone on the internet can see the files uploaded here"), which is also the only documentation this process has — for a service this small, nothing separate is worth keeping current |
| Member feedback triage | 3 | 3 | 3 | watch | Was recorded stable; a 2026-08-25 measurement showed it was not. Staff said at the 08-25 meeting that they submit things and never hear what happened. Of 324 submissions ever made, 139 (43%) had no entry on the board at all, the four honest-closure statuses had been used zero times in 174 items, and 82 open items had gone untouched for over three weeks. The mechanism was never broken — items that reached the board did get flipped on deploy — the failure was coverage and follow-through. Backfill took coverage 57% → 99% (302 items); 55 of the missing 139 turned out to be already fixed and nobody had said so. First full pulse under the new loop (2026-08-31) came back clean: every submission reached the board (2 unledgered, both known junk), the three oldest stalled items were given real answers rather than another status bump, and the three people who reported them were emailed automatically. One clean cycle is not yet a closed watch window — hold watch until a second. SHIPPED LIVE 2026-08-26: submitters now get a receipt and a status-change email, the form gained a staff alert (it previously had zero handlers, so submitting notified nobody), the board sorts by reported date, and weekly-triage gained a look-back step because step 1 only ever looked forward from the watermark — which is exactly how the gap opened. Two defects were caught on test before live: publish: false never worked at all (setPublished() takes no argument in D9+, so it silently published — all 303 ledger items and 124 changelog entries on live are published, nothing was ever held back), and the staff email re-sent on every manifest re-run. Watching for: whether the aging count actually falls, and whether the 66 not_actioned items get decided rather than becoming a new silent bucket. Cycle plan routing all 180 open items into 12 work packages at docs/ops/FEEDBACK_CYCLE_PLAN_2026-08-26.md. Originally: a well-documented A3 — worth copying as a pattern |
| Quarterly close-the-loop reporting | 1 | 1 | 3 | planned | Telling members each quarter what changed because of the feedback they gave. Nothing built.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Staff view-as-member support tool | 3 | 2 | 2 | stable | Added 2026-08-31 by the weekly pulse — we had never mapped it, and it was mapped because it appeared to be broken. When a member says "the site is doing X", staff reproduce it by viewing the site as that member rather than guessing. A staff member reported on 2026-08-26 that it had vanished ("vital functionality... please fix asap"). It had not: the 2026-08-28 release added two permissions to the facilitator role, and staff already signed in kept serving stale permission state until they signed out and back in, which restored it. Verified working the same day with the reporter. The real finding is not this tool but the class — a release that changes staff permissions can silently degrade what already-signed-in staff can see, and it presents as a familiar feature disappearing. Second recorded instance. Deploy process now owes staff a re-login notice on any release touching roles |
| Year in review | 4 | 3 | 1 | stable | A personal end-of-year summary for each member — what they made, learned and attended.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
The conversion funnel: getting people who are not yet members in the door and across the line — tours, waivers, follow-ups, campaigns. The line against Communications is the audience: if it exists to convert outsiders, it is here; if it is a standing channel talking to people we already have, it is Communications. v1 folded the two together and lost the whole funnel, which is why they are deliberately separate.
| Process | Auto | Doc | Impact | State | Notes |
|---|---|---|---|---|---|
| Tour booking & delivery | 3 | 2 | 4 | stable | All staff sit on a published tour schedule. JR is considering moving tours to a volunteer corps, which would change both the capacity and the training need |
| Tour follow-up & conversion | 2 | 2 | 4 | optimizable | Conversion-Focused Marketing and Follow-Ups wants automated personalised follow-up. Currently manual or absent |
| Guest waiver → member conversion | 1 | 1 | 3 | optimizable | 2.4% against a 5% goal on 218 waivers. No defined follow-up at all |
| Workshop participant → member conversion | 2 | 1 | 4 | optimizable | JR: it should send an email and re-invite attendees, but that needs verifying and is undeveloped either way. 6.4% conversion against a 10% goal, and the strategic plan carries its own note that this is not properly measured. |
| Discovery source capture | 3 | 2 | 2 | optimizable | "How you heard" is collected and discussed in the outreach and marketing committee. Self-reported, so treated as directional rather than reliable. |
| Referral / bring-a-friend | 2 | 2 | 4 | broken | It runs, but by hand. Members do refer; JR names it explicitly as an area for technical process improvementRaisedJR Logan, 2026-08-27 — asked for the referral process to be reviewed, improved and automated |
| Ambassador program | 1 | 1 | 3 | planned | A named group of members who bring people in and welcome them once they arrive, with some recognition for doing it. Nothing built.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Community tabling & partner events | 2 | 1 | 2 | stable | Staffing a MakeHaven table at community and partner events to reach people who have not heard of us. It runs, but how it is planned and staffed is not written down anywhere.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Community partnership development | 2 | 1 | 3 | stable | Confirmed running, though without a defined pipeline or count. |
| Media relations & press kit | 1 | 1 | 2 | planned | A maintained press kit and a habit of pitching stories, so coverage is sought rather than waited for. Nothing built.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Neighborhood & postcard campaigns | 2 | 2 | 2 | stable | Run as experiments rather than on a schedule — planned ad hoc rather than triggered. |
| Paid digital advertising & retargeting | 1 | 3 | 3 | changing | 250 hrs budgeted. Ad Grant campaign launch-ready, blocked on account access |
| Lead / inquiry handling | 2 | 2 | 3 | optimizable | info@ lands in a shared Google inbox answered mostly by Kate, and staff struggle to keep up with the volume |
| Member phonebank campaigns | 3 | 2 | 3 | stable | Used once for a fundraising experiment, well received, and planned for the next phonathon. Occasional-use by design rather than neglected |
The standing channels — newsletter, digest, Slack, the site, the phone — and the hygiene that keeps them working. These speak to everyone we already have; anything built to convert outsiders lives in Outreach & Recruitment. The two overlap at the newsletter and social media, which serve both audiences: those sit here because the channel outlives any one campaign.
| Process | Auto | Doc | Impact | State | Notes |
|---|---|---|---|---|---|
| Weekly digest | 5 | 3 | 3 | watch | The W34 send stalled and nothing caught it. On 2026-08-19 a 892-recipient batch sat for 4h45m with zero delivered, blocking every other CiviMail behind it. It left no error to find: PHP's max_execution_time killed the send rather than it throwing, so every error-log-based monitor we had was structurally blind to it. Resolved by raising mailerBatchLimit 0 → 100 so the batch fits inside the execution ceiling — a CiviCRM setting, not Drupal config, so it does not travel with drush cim and must be set on any environment that will send at scale. Alerting shipped live 2026-08-20 (makerspace_digest_scheduler 7ad888c): two checks on every cron tick — a running job whose delivered count stops moving, and a schedule whose window passed with no mailing created at all, the failure that leaves no row and no job to find. Alerts reach Slack, staff@makehaven.org and watchdog. The same release fixed an ordering bug that had recorded no run-state for W31–W34, so status had been reading a month stale. A moves 4 → 5: failure now reaches a human who was not looking for it. Watching: the 2026-08-26 send, and whether the missed-send check stays quiet on live rather than crying wolf Capped-email release SHIPPED LIVE 2026-08-28. Eight full cards, then up to 12 sparse lines in a new "Also Happening" block, then a full-calendar button; interest matching moved to child terms only; and the subject line is now drawn from the last_chance block every recipient receives identically, so it can no longer name an event absent from a reader's copy. The block needs a manual CiviCRM edit that no config import performs — {events_email_listing.also_happening} in message template 176 — rehearsed on test after the live-DB clone and then applied on live; verified rendering 7,039 bytes / 10 links for a real group-64 recipient on both. Now also watching the Wed 2026-09-02 send: total clicks against the 57–71 baseline, since capping the long tail is the risky half. Announced as changelog #44797. 2026-09-03 — the digest had been advertising the urinal deodorizer. Kate raised it after the 09-02 send, seconded by Lior and Ashley. The "New activity" block's bundle filter on views.view.digest_weekly_content included material, the store inventory type, and measured on live 11 of the last 12 eligible nodes were store restocks — so that block was a restock feed in practice, not an editorial one. material removed from that display only; featured keeps it, since it requires promote = 1 and a staff member can still feature one deliberately. Shipped live the same day with the notices block ({events_email_listing.notices_recent}) inserted above it, so staff-written news now occupies the slot restocks were filling. Announced as changelog #44943. Watching: the 2026-09-09 send, for whether that block now carries anything a member would care about — the risk is not noise any more but emptiness, since nothing else was being created. |
| Monthly newsletter | 3 | 3 | 3 | watch | First full-list issue is a draft on live as of 2026-09-03, and a human sends it. Mailing #148 "MakeHaven Monthly — September 2026" exists on live built from template 200, include group 66 exclude 64, with scheduled_date NULL and no approval, so the draft is inert (note, corrected 2026-09-05: a job-row count is NOT the safety test — a test send creates a job row and computing recipients fills civicrm_mailing_recipients, which together read like a premature submission. The honest test is scheduled_date and approval_status_id both NULL with no non-test recipient queued); monthly_active is false, so our own scheduler ignores it. Draft-only cron shipped live 2026-09-09, flag still deliberately OFF. The reason it could not be switched on was code, not editorial: cron called the create-and-send path under a different mailing name from the draft staff edit, so flipping it would have mailed the raw template to ~6,000 people and left the edited issue behind. The monthly path now only builds a draft and emails alert_email a link. Proven on test before shipping, against the honest test this row already insists on: with the flag forced on, cron created a mailing with scheduled_date NULL, approval_status_id NULL and zero job rows, and cron correctly skipped while that month's issue already existed. A near-miss worth recording: committed config carried alert_email: '' against live's staff@makehaven.org, so the config import would have blanked the very address this feature emails — caught during reconciliation, and the feature would otherwise have shipped mute. monthly_active is now safe to flip and remains JR's call; flipping before 2026-10-01 points the first run at September's already-sent issue too. It was created this way deliberately: drush mh:digest-create-monthly --force has no draft stage — DigestScheduler calls submitMailing() then processMailingNow() in one execution and backdates the schedule, so the documented "first send" path would have mailed 6,600 people with nothing a human could inspect in between (and it still errors on test, since 2026-08-18). JR's call 2026-09-03: a human approval step until this is dialled in. Two things landed the same day that make an issue writable at all: a Notice content type (/node/add/notice) so staff write member news as site content that feeds both newsletters from one source instead of typing it into Mailchimp, and a newsletter console at /admin/makerspace/newsletter with a preview that renders the real email and can send a test copy — previously the only way to see an issue was to mail it. Both previews render against live data with 0 unresolved tokens. A moves 2 → 3 and D 2 → 3: the issue can now be seen and tested before it goes, which was the missing feedback loop. Watching: the Saturday 2026-09-06 send — whether Kate can edit, preview-as-a-contact and test without help, and what delivery looks like against the weekly baselines. Open: --no-send on both create commands and taking the send out of --force for the monthly, so the accidental mass-send path stops existing rather than being avoided by convention. Preview as a specific contact, never as yourself — rendered_view is interest-filtered, so a staff copy is not representative. |
| Email bounce & suppression handling | 4 | 3 | 3 | watch | Current2026-09-09 release evidence: receiver and suppression backfill are live; the external webhook configuration remains an open action in the handoff.Next actionJR configures the Broadcast-stream webhook; Kate follows up affected contacts in restricted records.Next reviewAfter webhook configuration, then after the next mailing.AcceptanceConfirm an actual provider event reaches the receiver and updates the intended contact; reconcile subsequent mailing results. Deployment alone does not establish delivery. |
| Mailing list / smart group hygiene | 3 | 2 | 3 | watch | Verified against live 2026-08-17: the three repairs are holding. All the mailing feeders are smart, all rebuilt their caches within the hour, and every audience has grown since the 2026-08-06 fix (Monthly 7,373 → 7,484, Weekly 876 → 946). The reason this kept recurring was never the groups themselves but that nothing was watching — each freeze was found by hand, months late. Two checks were added to scripts/workflow-health.sh on 2026-08-17 to close that: one for a smart group whose cache stops rebuilding, one for a group fed by an automated write path that has gone quiet. The second immediately named a real outstanding case the audit had left behind — the Guest Waiver webform's group-add has been dead for 145 days, harmless only because that audience is now activity-driven. Leaves watch when a freeze is caught by the check rather than by a person |
| Slack announcements | 4 | 2 | 2 | stable | Tasks, events, asset status |
| Slack membership lifecycle sync | 4 | 3 | 3 | watch | Current2026-09-10 release evidence: the Slack entry page is live; automatic invitation cutover remains inactive. This summary does not establish that the full lifecycle is stable.Next actionJR supplies a shared invitation link accepting personal domains and chooses the cohort date; the deployment session then prepares the controlled joining test.Next reviewAfter the link and cohort date are supplied, before retiring the existing invitation provider.AcceptanceOne controlled member excluded from the old sender receives Drupal mail, joins and receives the intended channels. Separately verify reconciliation before closing the full lifecycle watch. |
| Website content publishing | 2 | 1 | 2 | stable | Any staff member can publish; there is no editorial calendar. JR wants to step up blog and announcement output, which would also feed the developing automatic newsletter |
| SEO & analytics instrumentation | 4 | 3 | 2 | watch | Live 08-09. Tag wiring is implicit and fragile |
| Inbound phone & voicemail triage | 1 | 1 | 3 | changing | Moved from optimizable — work is landing, not merely wished for. JR: "this is a weak point." Calls go to a Google voicemail box that often sits unanswered for a long time because of staff capacity. The voice AI routing track shipped its Phase 1 foundation live on 08-07, inert behind config; the Voicemail-Tool is written and pre-launch, awaiting a Twilio number |
| Impact storytelling & member spotlights | 1 | 1 | 2 | planned | Publishing member stories on a regular cadence to show what the space makes possible. Nothing built.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Annual report & impact reporting | 2 | 2 | 3 | stable | The annual member meeting is presented and recorded, and a PDF of that deck serves as the annual report when one is requested. Standards S060 wants finances, participation, outcomes and setbacks — worth checking the deck against that |
| Process | Auto | Doc | Impact | State | Notes |
|---|---|---|---|---|---|
| Contractor payment → Xero bill | 4 | 3 | 4 | watch | Duplicate-bill fix pushed, deploy pending; no real double-pays occurred. Covers member reimbursements as well as contractor hours. Two more fixes staged 2026-08-15 off a member complaint: the "My Payment Requests" list showed neither the amount nor the hours, so you could see you had filed something but not what you were owed; and the request title — which SyncManager sends to Xero as the bill line description — was being blanked by every background save, because the label pattern rendered access-controlled fields as the anonymous account cron runs as. 290 of 307 requests had lost theirs, and those bills are already in Xero carrying the damage. Both shipped live 2026-08-15 (xero_bills_sync ac54dcb): the member list now carries For / Hours / Amount, the label pattern moved to property tokens so background saves stop rewriting titles, and update_9005 rebuilt all 311 titles on test and live. Verified as a real member, not as an admin — field permissions make those two views differ. Watching: that titles survive the next 1st/15th consolidation run, since cron is what erased them before. Staged 2026-08-18 (a1748d7): the free-text "Pay to (Xero contact)" box no longer sits in the middle of every request form for staff — it moves into the collapsed Advanced group and only comes back out when the payee has a business contact recorded. That review found the LLC billing path had been entirely dormant since it shipped — no account had a business Xero contact set — and JR recorded the first one the same day (Aaron Monikowski → Glow Worm Ideas, live). Two things surfaced from switching it on: his form save silently failed to land (written via drush instead; cause unidentified, worth chasing if it recurs), and the choice label rendered the raw Xero ContactID, which a second fix resolves to the business name. Scope, recorded 2026-08-28 from the Q1 build: xero_bills_sync shipped 2026-01-08 carrying four distinct real-world payment paths — a contractor invoicing hours, a member or volunteer out of pocket for materials, an instructor or contractor filing a timesheet, and a mileage claim. This row is the first two; timesheets sit under Instructor stipend payment and mileage now has its own row. The module was the most-developed piece of infrastructure of Q1 2026 — 22 reference bumps between 2026-01-09 and 2026-03-20 — and the board report described it as the quarter's finance automation launch |
| Mileage reimbursement | 4 | 3 | 2 | changing | Added 2026-08-28 by the Q1 module sweep — nobody had a row for it. Someone uses their own car for MakeHaven — a supply run, collecting a donated tool, teaching off site — and is paid back for the distance. The finance automation shipped 2026-01-08 included a mileage calculator as one of its four payment paths, so a trip is now entered once and reaches Xero as a bill line on the same approval path as every other request, rather than being worked out by hand and sent as an email. Scored from the mechanism, not from use. The calculator exists and is wired to the approval path; nobody has checked whether a single mileage claim has been filed since it shipped. That check is the next move, and if the answer is none then this is planned, not changing — a built path nobody uses is not an automated process. D1 because nothing tells a member that the option exists, what rate applies, or when it is appropriate to claimDescription inferred 2026-08-28 — not confirmed by anyone who runs it |
| Vendor payment via Melio | 2 | 2 | 4 | optimizable | JR, 2026-08-15: "actually working pretty good… we have fixed most issues." It is not failing, so broken was the wrong word; what is left is an end state someone can name and has not been reached, which is what optimizable is for. There is still no periodic sweep — detection depends on someone happening to check, which is how two payments once sat undelivered. The failure is visible in Xero if someone looks, and responsibility sits with the staff member who initiated the payment to their own contractor, per the procurement policy's budget-line ownership. |
| Stripe → Xero reconciliation | 3 | 3 | 4 | stable | Statements import into Xero, the bookkeeper matches receipts, and unresolved items are worked through in a standing session with the executive director. The known friction is unlabelled PaymentIntents, where the bank feed shows only a charge description and the income line has to be inferred. |
| Monthly financial close | 2 | 3 | 4 | stable | A contractor does data entry, the executive director handles the harder work, finance committee volunteers assist, and a CPA reviews and prepares the 990 |
| Budget vs actual review | 2 | 3 | 4 | stable | The board approves an annual budget and receives regular budget-versus-actual reporting |
| Reserve fund management | 2 | 3 | 5 | stable | Governed by published financial and procurement policies, managed by the executive director. Reserve stands at 2.93 months against a 6-month goal |
| Annual external review / audit | 2 | 3 | 4 | stable | Completed last year; this year's documents are already with the reviewer |
| Financial policy maintenance | 2 | 3 | 3 | stable | Both policies are published and current |
| Financial dashboards & board briefings | 2 | 2 | 3 | watch | The executive director prepares the board packet from Xero, working documents and the KPI dashboard. 2026-09-09 code and aggregate-source review: KPI freshness/fallback labels, chart-range exports and clearer outreach outcome wording are being staged; they are not Live yet. Follow-up calculation repair reconciled the August anniversary cohort at 23/37 and current Chargebee contribution from 282 recent joiners at $13,148/month against independent source checks. These calculation changes are staged for the next batch, not Live. Current cohort MRR is explicitly distinct from historical acquisition MRR; legacy targets are hidden. Existing reporting practice is preserved; validate the revised dashboard in the next release 2026-09-09 operational reporting fixes staged, not deployed: Lending ee72e74 aligns calendar-day overdue and 12 completed months, withholds unsupported retention/LTV claims; Storage e9e2d16 separates unassigned list value and surfaces record exceptions before a filtered roster; Member Success 671d04a fixes contact timing, range/export agreement and snapshot/financial labels. Local 53 unit tests, 3 MariaDB kernel tests and 2 browser journeys pass. Assignment conflicts and missing historical evidence remain; no billing/delivery changes or human re-verification. See website release 2026-09-09-operational-dashboard-corrections.md. Snapshot repair staged 2026-09-09, not Live: September capture stopped on case-equivalent plan keys; headers concealed absent facts. Snapshot 92be39e adds atomic writes, fact-level health, correct storage field mapping and bounded historical recovery; Member Success 7caf648 clears obsolete latest flags. Forty kernel tests pass. Local recovery restored only original-count KPIs and dated donation facts; remaining history stays missing until archived evidence is available. Production recovery/deployment are pending; see website release 2026-09-09-snapshot-integrity-repair.md. 2026-09-10 bundle shipped live — changing → watch. Three faults fixed at once. (1) The monthly snapshot had failed on Sept 1 on a duplicate plan key, so KPI history stopped at Aug 1 and storage facts at July — while the health command reported OK, because headers counted as coverage and empty KPI sets bypassed the check. Capture is now all-or-nothing with rollback, retries the same day, and reports missing data honestly. (2) Retention and revenue were being calculated wrongly: retention now follows each member's own anniversary with leap-day clamping and includes disabled accounts (Aug 2025 reconciles at 23/37), and revenue now states plainly that it measures the current Chargebee cohort's monthly contribution, not historical acquisition. (3) Every KPI now shows when it was calculated and flags figures needing checking. The September facts were recovered on live the same day — KPI facts 0→2, donation 0→1, original org row preserved unchanged at 777 active / 845 total, re-preview empty. Operational note that bit us and will bite again: drush msd:kpi-warm must run after any deploy touching this. A cache clear alone does not refresh persisted payloads, so without it 46 KPIs render "Awaiting refresh to verify the value source" and goal comparisons stay hidden — it looks exactly like the dashboard has stopped calculating. Found because JR asked why /kpi showed calculation timestamps. Watching: whether the Oct 1 monthly capture completes unaided (the first real test of the retry), and whether health goes quiet or keeps warning on the plan/revenue/storage/certification/access history that was NOT reconstructed — that remains separate data work. |
| Donation processing | 3 | 2 | 3 | watch | 2026-08-26: the content port ran on live — 14 dead connect.makehaven.org links across 10 nodes and 1 message template repaired, re-run reports 0, and /donate markup now contains none. Watching for a human confirming the Opportunity Fund card is clickable end-to-end; the Pantheon domain removal and the Cloudflare connect A record are still open. Prior state below. 2026-08-24 — one of the two donation routes on /donate has been dead, and nobody caught it. The Opportunity Fund card ("help make membership accessible to everyone") had its icon, heading and description all linking to connect.makehaven.org — the standalone-CiviCRM domain retired when Civi was merged into the main site. That host does not merely redirect badly: its HTTPS does not resolve at all (no certificate; it is the standing action_required in terminus domain:list) and plain HTTP returns Pantheon's 404 Unknown site. So anyone clicking that tile got a connection error, and it produced no server-side error on our side to find — the failure happened entirely in the donor's browser, which is why months of donate-page feedback circled the page without anyone naming this. Node 328 carried three more of the same links; 14 dead links in total across 10 nodes and one CiviCRM message template. Paths map 1:1 (www.makehaven.org/civicrm/contribute/transact?reset=1&id=2 returns 200), so the repair is a host swap: config fix for the staff CRM-contact view plus an idempotent content port, staged 2026-08-24, deploy pending — it writes content, so it must run on test and live. Moves back to stable once the card is verified clickable on live. Found while retiring the dead domain, not by anyone testing the donate page. The acknowledgement half is unaffected and still works: The acknowledgement procedure is followed. Some manual steps are deliberate — handwritten notes on cards — and JR sees room to automate acknowledgements for smaller donations while keeping the personal touch for larger ones. |
New group in v2. Has a committee, a dashboard, and five strategic objectives; had zero rows in v1.
| Process | Auto | Doc | Impact | State | Notes |
|---|---|---|---|---|---|
| Annual appeals (spring + year-end) | 2 | 2 | 4 | stable | Run by the Resource Development committee with Kate as staff lead |
| Recurring giving program | 2 | 2 | 3 | stable | Owned by Kate with the resource development committee; 39 recurring donors against a 60 goal. |
| Major donor LAI qualification & pipeline | 3 | 2 | 4 | changing | Sponsorship-Tool implements LAI scoring and a pipeline board; 3 tasks from done |
| Major donor stewardship cadence | 2 | 2 | 4 | stable | Staff steward major donors and the board is sometimes involved. JR: the system could be built out — cadence is not yet systematic |
| Corporate sponsorship solicitation & renewal | 2 | 3 | 4 | watch | Tier ladder + benefit matrix live 08-09; round 3 staged. $7,916 against a $25,000 goal |
| Sponsor recognition & benefit fulfilment | 2 | 2 | 3 | stable | Kate leads, shared with the executive director for sponsor relationships. |
| Grant pipeline & deadline tracking | 3 | 2 | 4 | stable | Major correction, now verified against the database. JR was right and the KPI is wrong. civicrm_value_funding_7 holds 46 records; 6 have 2026 due dates and 5 of those 6 carry a submitted link. So roughly five grants were submitted this year, not zero. The win ratio KPI is fine (12 won of 34 decided = 35%, reported 32%), so this is one broken metric, not a broken dashboard. The pipeline has no submitted status at all — it runs researching → waiting → won/lost/abandoned — so "submitted" is inferred from a link field and the period is keyed off the due date. That inference is what fails |
| Grant outcome recording | 3 | 2 | 3 | stable | Recorded in CiviCRM by Kate and the executive director; win ratio 12 of 34 decided. |
| Donor data hygiene in CRM | 2 | 2 | 3 | stable | Kate owns donor data quality and does most of the checking, shared with the executive director for major donors and sponsors. |
| Case statement maintenance | 1 | 1 | 2 | planned | Keeping a current case for support — why a funder should give — instead of writing one under deadline for each ask. Nothing built.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Planned giving / bequest program | 1 | 1 | 2 | optimizable | A page exists but there is no cultivation activity behind it — present in form, dormant in practice. |
6 rows in v1, 17 here. This is where the strategic plan added the most, and where almost nothing is defined.
| Process | Auto | Doc | Impact | State | Notes |
|---|---|---|---|---|---|
| Board recruitment matrix & sourcing | 2 | 2 | 4 | stable | A webform is sent to candidates to collect skills and demographic information, which feeds the matrix used to build the slate. |
| Board nomination & election | 2 | 2 | 4 | stable | The governance committee discusses; nominations arrive through a website form; candidates are assessed against the skills matrix; the committee talks with the strongest, assembles a slate, and the board passes it. |
| Board onboarding | 2 | 2 | 3 | stable | A new director receives a one-hour orientation presentation with Q&A. |
| Board self-assessment (annual) | 2 | 2 | 3 | changing | In progress now via a board survey, after a long gap |
| Board & committee minutes retention | 2 | 2 | 4 | stable | Board meets quarterly plus committees; approved minutes retained in Google Drive. Candidate for Governance-Dance if that app is adopted |
| Governance policy annual review | 2 | 3 | 5 | stable | Conflict-of-interest policy published; the policy set is indexed in the operations doc |
| Committee charters & effectiveness | 2 | 2 | 3 | stable | Correction: the DEI committee is active and meeting — v2's "no active members" is stale. Committee structure is published. Real remaining gap, in JR's words: no reliable way to track who is actually on each committee — the clearest use case for Governance-Dance |
| Governance archive / institutional memory | 1 | 2 | 3 | planned | Partly served by Google Drive and the operations index today, but nothing purpose-built. JR wants something stronger — possibly Governance-Dance, possibly a dedicated continuity tool. |
| Board officer & director succession | 2 | 3 | 4 | stable | Split out of the old succession row — and it turns out to be defined, in the bylaws. Section 2.4 covers director resignation, 2.5 removal, 2.6 vacancies (fillable by the directors at any meeting), and 3.7 officer resignation and removal. Officers serve one-year terms and "interim vacancies may be filled by the Board of Directors to serve until the next annual meeting". Directors serve staggered three-year terms with roughly a third expiring each year, so continuity is structural rather than ad hoc. (Verified against the bylaws, 2026-08-14) |
| Executive succession & emergency authority | 1 | 1 | 5 | undefined | The real gap, now isolated. The bylaws handle governance continuity; nothing handles operational continuity. If the executive director were suddenly unavailable there is no documented answer to who signs, who approves payments, who holds the vendor and landlord relationships, or who assumes day-to-day authority — JR: "not really, we should work on this." The partial mitigations are real but incidental: system access is distributed and recoverable through the org email account, and a MakeHaven System and Data Access Agreement form has captured 21 submissions since May 2025. Standards S009 asks for succession and emergency authority to be documented and tested — we have neither. This is now the highest-impact undefined process in the registry. |
| Staff onboarding / offboarding | 1 | 2 | 3 | stable | Reframed. The same four staff have been in post for years, so this rarely fires; when it does, the executive director spends substantial one-to-one time. JR's own redirect is the useful finding: the real onboarding gap is instructors and volunteers, not staff — and those are far higher-volume. |
| Staff development, benefits & wellbeing | 2 | 3 | 3 | stable | Annual evaluations happen and job descriptions are reviewed at the same time; both live in Google Drive |
| Staffing plan & role clarity | 1 | 2 | 4 | optimizable | JR: "not really formally… I feel like we have it but not in one place." Individual job descriptions exist and are reviewed at annual evaluations; the org-level plan does not exist as a single artefact. |
| Volunteer pathway, roles & recognition | 2 | 2 | 3 | stable | Roughly 75+ titled volunteers across facilitators, shop techs, the lending librarian, ambassadors, board and committee members. Structure is published; the gap is a single maintained roster |
| Annual member meeting | 2 | 2 | 3 | stable | Held with a recorded presentation; the PDF of that deck doubles as the annual report when one is requested |
| ED time reporting to board | 2 | 3 | 2 | changing | New Aug 2026, from a 5-day pilot. Also the only real source for the effort_hrs_month field this registry is missing — worth wiring the two together rather than estimating twice |
| Harassment & conduct complaints | 3 | 3 | 5 | stable | Intake is a Google Form — chosen deliberately for privacy control rather than the site's own webforms — which emails staff, who act immediately. Gap: no case record or tracking beyond the mailbox |
| Board–member engagement | 1 | 1 | 3 | optimizable | A member comment states the board "fails every transparency test I can think of"; JR's reply agrees a stronger feedback loop is needed |
| DEI review (inclusion, accessibility, affordability) | 2 | 2 | 3 | stable | The DEI committee is active again and the accessibility walk-through runs annually alongside the safety one |
| Records retention & filings calendar | 1 | 2 | 5 | optimizable | Filings are now documented in the renewal calendar, but JR confirms there is no retention rule of any kind yet — "need to establish." That blocks the Standards three-years-of-evidence test and leaves no defined answer to what we keep, for how long, or where. |
| Nondiscrimination & accommodation requests | 3 | 2 | 4 | stable | Requests go to the shop manager and director, and there is a dedicated accessibility-issue intake alongside the general concern pipeline. |
| Member IP rights | 2 | 3 | 3 | stable | A policy does exist. The membership agreement states members keep their own IP, which satisfies Standards S008. |
| Demographic data collection & reporting | 4 | 3 | 3 | stable | Lives in CiviCRM since Dec 2025; dashboards converted 07-10 |
How we change everything else.
| Process | Auto | Doc | Impact | State | Notes |
|---|---|---|---|---|---|
| Feature planning | 2 | 3 | 3 | stable | Tracks carry a premise and kill criteria since 07-09 |
| Deploy pipeline | 4 | 4 | 4 | stable | The model for what stable looks like. An SOP others have run, a wizard that enforces it, a ground-truth state file. 2026-08-24 — closed a real gap in it: the pipeline treated "code is on live" as done. It never asked whether the change was actually switched on or told to anyone, and both failures are silent by construction — nothing errors, the work simply never reaches a member. Three features had shipped and sat dormant behind a flag (monthly newsletter, voicemail routing, Slack intro banner), and the 08-21 workspace floorplan — the most visible thing in that release — shipped with no changelog entry and was spotted by chance three days later. deploy-status.sh now has a Section 6 that reports declared FLIP: lines whose live value does not match intent, and shipped DEPLOY_TODO entries carrying no ANNOUNCE:/NO-ANNOUNCE: decision; the deploy skill declares the flip at stage time (the only moment anyone knows what a flag is for) and refuses to close out a release while Section 6 is dirty; the weekly pulse reads it between deploys. All 18 shipped entries were backfilled so the check starts clean, because an alert that is noisy on day one gets ignored 2026-08-28 — a third way "shipped" fails to mean "delivered", and this one the pipeline cannot see at all. Section 6 catches a flag nobody flipped and a change nobody announced; both are server-side facts we can query. This one is not. The price-free workspace floorplan deployed correctly — the file on live hashed byte-identical to the intended drawing, verified from the command line — and the person who requested it still saw the old priced plan under the new overlay, because Pantheon serves module assets with cache-control: max-age=31622400 and the filename had not changed. A year of browser cache, no server-side invalidation, and terminus env:clear-cache reaches none of it. Every check we run passes while the change has not reached anyone, and it would have stayed that way silently for months had JR not looked at the page within the hour. Fixed for this asset two ways (a versioned filename, plus a content hash stamped onto the href at render time so a future redraw self-busts), but the general lesson is unowned: any module asset replaced in place under the same name has this problem, and nothing in the pipeline warns about it. Worth a git diff --stat check for changed-but-not-renamed files under web/modules/custom/*/assets/ at deploy time. |
| Config reconciliation | 3 | 3 | 4 | stable | Known gotcha: it deletes committed staged config |
| Security audit | 3 | 3 | 5 | stable | Quarterly. Four of six dimensions never run Cadence observation, 2026-09-10 (not a failure of the audit). Dimension 5 (composer audit) is the only thing that catches contrib security advisories, and it runs quarterly — so an advisory can sit unpatched for up to a quarter. This deploy found three moderately-critical ones published 2026-09-02 (drupal/ai XSS + access bypass, ai_translate access bypass), all on enabled modules, and caught them at 8 days only because a deploy happened to run composer audit in its prep. Patched same day to ai 1.4.8 / ai_translate 1.4.1. Worth deciding whether dependency-advisory checking should run on its own faster cadence rather than riding the quarterly sweep. |
| Weekly pulse triage | 3 | 3 | 2 | watch | The weekly pass over incoming signals — tickets, reports and alerts — deciding what gets attention that week.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| Cycle review | 2 | 3 | 3 | stable | Replaced a 950-line improvement pipeline that was fully specified and never ran once |
| Backup & disaster recovery | 3 | 3 | 5 | stable | Three systems hold essentially everything: Google Drive (Google for Education) for documents, Pantheon for the website with its own robust backups, and GitHub for code — which has repeatedly been the actual recovery tool. UniFi cameras are a fourth, lesser store. Each vendor carries its own recovery documentation, and JR has performed real restores. What is missing is a MakeHaven-side runbook saying which to restore in what order. |
| Scheduled job execution | 4 | 1 | 4 | optimizable | Added 2026-08-18 by the module sweep — almost everything that runs on a timer rides on this and nothing had a row for it. 57 registered jobs run under Ultimate Cron behind an hourly external trigger: the Chargebee status sync, the UniFi access sync, the Xero bill sync, the Slack member sync, the weekly digest, the monthly membership snapshot and the rest. It works — system_cron completed 24 passes on every full day in the retained window. What is missing is anything watching it. The launcher logs "No free threads available for launching jobs" roughly 57 times a day, every day (554 across nine days). That is invocations arriving while a pass is still running rather than the hourly pass being lost, but it means a real stall would look exactly like the noise we have learned to ignore. Separately makerspace_ai_tool_context_cron has sat disabled since 2026-07-08 and nothing reported it. The end state someone can name: a job that misses its own window tells a person, instead of a person noticing |
| Production error monitoring | 2 | 2 | 4 | optimizable | Added 2026-08-20 by the cycle review — three separate rows already carried a "we could not see it fail" finding and nothing named the underlying process. How anyone learns that production is broken. In practice: the weekly pulse reads the watchdog trend, scripts/daily_log_triage.py runs a pass, and a handful of modules alert into Slack. The signal it all rests on is badly degraded. Over the 12 days watchdog retains, 355,028 of ~410,000 rows are a single warning — Theme hook %hook not found., the known search_api shutdown-phase flood whose 2026-08-03 fix missed site_content and never stopped, at roughly 29,600 a day. Add 32,315 debug-level access_display rows for a service account and the bot-driven 404s below, and real errors are about 0.2% of the log: 533 CiviCRM, 552 UniFi, 130 geocoding, the rest in single digits. Two consequences, one obvious and one not: triage means reading past a wall of noise, and the volume is itself a database write load on a site with standing performance complaints. The pattern this row exists to name: the W34 digest stall left no error at all because PHP killed the send rather than it throwing, so every error-log-based check was structurally blind; the late-fee refunds were first re-recorded as still broken because a search for new failures found none, when a search for successes showed they had gone through. Absence of errors is not evidence of health, and nothing here is built to know the difference. Update 2026-08-24 — the flood stopped on its own and the instrument is usable again. Theme hook %hook not found. fell from ~40,000/day to 3,526 on 2026-08-18 and to zero since; nothing was deployed to fix it, so the credit goes to the 2026-08-18 CiviCRM recurring-event cascade fix rather than to the missed site_content patch. Retention recovered from ~2 days to 14.6 days, and the 315k theme rows still in the table are the pre-08-18 tail aging out. The 2026-08-24 pulse read the clearest severity≤3 trend in months — all known noise (UniFi listUsers 28–48/day, CiviCRM single digits, Calendly notices), with makerspace_dashboard geocoding also stopping after 08-20. This row stays optimizable rather than moving up: the noise was the symptom, and the structural point is unchanged — nothing here tells a human unprompted, and the same pulse still found the /store regression only because a member filed feedback, five days after it shipped. Scored A2 because a human starts every pass, D2 because the pulse and workflow-health docs describe it, I4 because the failures it misses are billing and delivery ones |
| Hostile traffic filtering | 2 | 1 | 3 | optimizable | Added 2026-08-20 by the cycle review. Keeping automated and hostile traffic off the site. Monitoring exists — the Pantheon traffic overage is checked weekly in workflow-health.sh — but nothing filters anything: there is no edge rule, WAF or rate limit, so every scanner request is served by PHP. The measurable cost is not just bandwidth. Bots hitting registration URLs for events that do not exist produced 444 CiviCRM fatal errors in 12 days, one URL (/civicrm/event/register?id=765) accounting for 358 of them and still firing; each is a full CiviCRM bootstrap that ends in a crash. The referrers are spoofed (bing.org, google.org, baidu.org — note .org), so this is not a broken internal link to chase. The 404 log agrees: the top paths are /.env, /api/.env, /graphql and a WordPress plugin exploit probe, at 100–230 hits each, alongside 52,930 access-denied and 36,087 not-found rows in the same window. Major correction 2026-08-24 — the overage was measured for the first time, and it is two problems, not one. Three days of live nginx logs (97,703 requests) show 54% of all traffic to the site comes from one IP: MakeHaven's own building (32.218.105.26, SNET), flat across all 24 hours. 39% of the entire site is three stuck loops on the card-test rig — a POST /user/login loop at 6,424/day that returns 200 every time and has never once authenticated (no Referer, no valid form token, so it never reaches login validation; watchdog records only ~110 real sessions/day site-wide), plus ~27-second polls of a tool permission and a card serial at ~3,100/day each. The UUID resolves to uid 3772 tester thetester — the test user the rig was moved onto on 2026-07-21, which fixed the member whose card was being charged and left the rig running. So the pages-served half of the overage is self-inflicted and needs no edge layer at all — stopping the rig is projected to take 1.15M → ~640k pages/month. The visits half is genuinely hostile traffic and does need the edge layer: 13,442 distinct non-building IPs in three days, 78% of them making exactly one request, 29% receiving only 403/404, each one billed by Pantheon as a visit. Also measured and worth acting on separately: the CDN cache hit ratio is 1.7–8.7%, so nearly every request reaches PHP — that is a performance finding, not a billing one, because Pantheon counts cache hits toward pages served. optimizable rather than planned because the watching half runs and the acting half was never built — the named next step is an edge layer, which also happens to be the only near-term way to restrict the unauthenticated door API (see API endpoint security) — and that step is now also unblocked, because the same logs answered the security ledger's standing question by identifying the reader egress IP The bot half was then measured too (same day), and every cheap mitigation is a dead end. robots.txt tops out at 3.9% of billed visits — 83% of the offending IPs present as ordinary Chrome and ignore it, and the declared crawlers are already obeying (7 of 1,790 touched a Disallowed path in three days). IP blocking is impossible: 8,753 IPs across 6,048 distinct /24s, with the top 50 /16 blocks covering 18.7% — a residential-proxy pool, not a range. And Drupal-side blocking moves the bill by exactly zero, because a 403 is still a request Pantheon served and counted. One real defect was found and fixed regardless: robots.txt ended with a second User-agent: group containing Allow: / after every Disallow, which last-match-wins crawlers read as permission to crawl everything. The lever that does work is a migration we simply have not done: the site still runs Pantheon's legacy Fastly CDN (via: 1.1 varnish), while Pantheon's next-generation Cloudflare-based GCDN went GA in July 2026 with bot protection included free and on by default — scoring each request, allowlisting verified crawlers, challenging the rest. Gated on two things: Terminus ≥ 4.1.9 (this machine is pinned at 3.6.2 by host PHP 8.1), and a written answer from Pantheon on whether challenged* traffic is still billed, which the docs do not state and which is the whole reason to do it. Recorded trap for whoever picks this up: our DNS is already at Cloudflare with the apex proxied, so orange-clouding www looks like a one-toggle fix and would genuinely stop the billing — but Pantheon discourages it and it breaks TLS certificate renewal, dropping the domain to Pending (Error). |
| Member-facing AI assistants | 4 | 3 | 2 | optimizable | Added 2026-08-18 by the module sweep. Two grounded chatbots are live: a member navigator that routes people to the right page for membership, resources, tools and projects, and a tool-page assistant that injects the live node data for whichever tool you are reading so the answers are about that machine. Both are link-first with a guard that strips invented URLs — it fired 4 times in the retained window, which is the point of having it. Context injection ran 325 times in nine days, but the AI request log records only one to five actual conversations a day through August 2026: built, live, and barely used. The named end state is the curated knowledge layer these are meant to read from. Measured properly for the first time 2026-08-20 by the cycle review, and three things changed the picture. (1) Answers were never being recorded at all. ai_logging writes output_text from the provider's raw output and the OpenAI chat provider leaves it empty — every chat row on live is literally {}, so the AI oversight console has been showing blank replies and answer quality was unmeasurable except when a member happened to ask a follow-up and the previous turn came back as conversation history. Which assistant answered and which user asked were not recorded either. Fix staged 2026-08-20 (makerspace_ai_tool_context 6a34121): the answer, a refusal flag, the assistant id and the uid now go into the log's extra_data. (2) The quality problem is concentrated and countable. Of the navigator conversations where the member sent a second turn, roughly 70% followed the assistant saying "I'm not sure" or "I don't know" — the follow-up is usually a member re-asking after a failure, not deepening a good answer. Asked how to submit a meetup it pointed at the CiviCRM event browser; the member replied "The answer is makehaven.org/meetup — learn that" and it said "I'm not sure" again. (3) The demand is wayfinding, not tool expertise. 24 navigator questions since 2026-07-01 cluster into five intents — room and door access (7), membership change (4), booking and facilitator hours (4), stuck in onboarding (3), profile editing (2) — while the tool-page assistant, which is on every tool and badge page for members and anonymous visitors alike, draws about three questions a month. The reach gap is the actionable half: the navigator is placed on six path patterns (/resources, /my-membership, /badges, /user/*) and none of /access-request, /appointments or the onboarding pages are among them, so a member sitting on the page where the question arises has nothing to ask. Nobody has ever rated an answer — the feedback table has 8 rows and has not moved since 2026-07-16. Separately, "Red", the anonymous recruitment assistant built to answer prospective-member questions without login, is switched off on the theme anonymous visitors actually get while still enabled in a theme the site does not use |
| Admin access & systems register | 2 | 2 | 4 | changing | A register has been started. Administrative access is distributed across staff and organization accounts. Account assignments and recovery procedures belong in the restricted systems register. Finishing the started document closes the largest remaining bus-factor gap. A MakeHaven System and Data Access Agreement webform has also been capturing who holds what since May 2025 (21 submissions) — a better foundation than the draft document alone suggested. |
| Local development environment | 2 | 3 | 2 | stable | Documented and repeatedly repaired |
| Preview / staff testing | 4 | 3 | 2 | stable | Sandbox site for extended staff testing |
| Policy & procedure document index | 1 | 2 | 4 | changing | The operations doc is the current answer and it is drifting. JR wants something stronger — possibly folded into Governance-Dance, possibly its own continuity tool. That decision is now live rather than hypothetical, because this registry is starting to do part of the job. |
| Process registry maintenance | 1 | 3 | 3 | changing | Current2026-09-10: JR selected a first pass to make current actions clearer and verification claims more precise.Next actionReview the pilot summaries with the staff who use these processes during the next existing staff review; record corrections through the registry feedback.Next reviewAt the next staff review, after these changes are published.AcceptanceStaff can identify the current situation and next action without reconstructing release history; check that submitted corrections receive a disposition. |
| Curated knowledge & Q&A layer | 1 | 1 | 3 | planned | A maintained knowledge layer — curated answers, indexed tool manuals — feeding site search, member support and the AI assistants, so answers come from one reviewed source instead of scattered pages. Named a high-priority initiative on the website master roadmap; no track yet.Description inferred 2026-08-16 — not confirmed by anyone who runs it |
| Makerspace Collective API integration | 1 | 1 | 2 | idea | Aligning MakeHaven's success metrics with the national makerspace collective's data aggregator — standardised measurement and collective grant power. Roadmap status: research. Related to but distinct from ecosystem partner data alignment, which is local.Description inferred 2026-08-16 — not confirmed by anyone who runs it |
| Screen & display fleet upkeep | 3 | 2 | 2 | watch | The Raspberry Pi displays around the shop run, but each is hand-built: no standard image, setup knowledge tribal. Two things changed in August 2026. The fleet was inventoried — six screens, their routes, refresh intervals and failure modes, written up at docs/ops/DISPLAYS.md, which is why D moves 1 → 2. And a shared self-heal runtime shipped live 2026-08-20 (makerspace_kiosk, first consumer access_display): the boards now poll with backoff, show a status chip carrying a ticking clock and a liveness dot, and reload themselves when a feed goes stale instead of sitting there looking healthy. That last part is the real fix — the member faces board failed silently, because its fetch errors were swallowed and its grid is append-only, so a dead feed rendered identically to a live one. A moves 2 → 3: recovery is automated, provisioning is still by hand. Watching: whether the chip reads green on the physical screens (still unconfirmed by a person at the hardware), and whether the remaining consumers — facilitator_display, makerspace_screen_slideshow, makehaven_tasks — get moved onto the shared runtime.Description inferred 2026-08-16 — not confirmed by anyone who runs it |
The CT Makerspace Network Standards of Excellence, as a lens on the registry: 72 of 84 standards map to at least one process here. The registry cannot score a standard — the assessment tool owns that — but it can do the drill-down the tool cannot: for each standard, which processes implement it, what state they are in, and which specific rows hold it below the next level. A standard whose processes are broken or unwritten cannot honestly reach level 2 (“written, assigned, consistently implemented, evidenced”) — so the To move up lines below are the work list for getting to standard, which is a concrete way of finding what we have underinvested in.
Mapping from docs/STANDARDS_CROSSWALK.md (draft, not yet reviewed).
11 mappings are starred in the source: the process does more than the standard
as written asks — feedback owed to the framework, not to us. The framework itself is a
draft under active development.
S001tier 1 · critical
Legal entity, tax-exempt status, registrations, and required filings are current.
S002tier 1 · critical
Governing documents define authority, elections, meetings, and dissolution.
S003tier 1 · critical
Board has at least five independent, unrelated voting directors or documents why another structure is appropriate.
S004tier 1 · critical
Board meets regularly and retains approved minutes documenting decisions.
S006tier 2
Whistleblower, records-retention, and ethics policies cover directors, staff, and key volunteers.
S007tier 2
Board-approved strategy connects mission, programs, facilities, and major capital purchases.
S008tier 2
Users retain intellectual property they create except where a written agreement clearly states otherwise.
S009tier 3 · critical
Leadership succession and emergency authority are documented and tested.
S010tier 3
Board completes a documented annual self-evaluation and reviews organizational risk.
Not yet mapped to any process: S005 — either a real underinvestment or a standard that does not apply here. Each is worth one honest sentence.
S011tier 1 · critical
Written general safety rules, emergency procedures, and emergency contacts are visible and current.
S013tier 1 · critical
Hazardous chemicals are labeled, stored compatibly, and supported by accessible SDS information.
S015tier 2 · critical
Tools are assigned risk levels that determine training, supervision, and access-control requirements.
S016tier 2 · critical
Injuries, near misses, unsafe conditions, and equipment damage are promptly reported and reviewed.
S017tier 2
Opening, closing, lone-work, impairment, and after-hours emergency protocols are documented.
S018tier 2 · critical
Insurance coverages and limits are reviewed annually against actual operations.
S019tier 3 · critical
A qualified annual safety audit tracks findings through closure and analyzes incident trends.
S020tier 3
Waste, emissions, used materials, and hazardous disposal are managed under documented environmental procedures.
Not yet mapped to any process: S012 S014 — either a real underinvestment or a standard that does not apply here. Each is worth one honest sentence.
S021tier 1 · critical
Every member completes orientation, accepts conduct rules, and signs required agreements before access.
S022tier 1 · critical
Restricted tools require verified practical training before independent use.
S023tier 2 · critical
Digital or auditable credentials control tool and facility access and can be suspended promptly.
S024tier 2
Dues, holds, guests, refunds, accessibility accommodations, and termination rules are transparent.
S025tier 2 · critical
Conduct and disciplinary procedures provide reporting, fair review, documentation, and proportionate action.
S026tier 3
Credential quality is periodically reviewed and portable credentials are accepted only through defined equivalency rules.
S027tier 1 · critical
Visitors register, accept safety/conduct rules, and remain within defined access boundaries.
S028tier 1 · critical
Staffing and supervision levels match the hazards available during public access.
S030tier 2
Accessible pathways help repeat visitors advance from introduction to independent capability.
Not yet mapped to any process: S029 S031 — either a real underinvestment or a standard that does not apply here. Each is worth one honest sentence.
S032tier 1 · critical
A current inventory identifies major equipment, location, status, and responsible lead.
S033tier 1 · critical
Unsafe equipment is clearly locked out and cannot return to service without authorization.
S034tier 2 · critical
Preventive maintenance schedules reflect manufacturer guidance, use, and risk.
S035tier 2
Original manuals, warranties, leases, insurance, access-control configuration, and critical records are securely backed up.
S036tier 2 · critical
Shop inspections verify egress, storage, guards, utilities, ventilation, and housekeeping.
S037tier 2
Critical digital services have named owners, backups, recovery instructions, and controlled administrator access.
S038tier 3 · critical
Annual asset audits reconcile inventory, condition, maintenance, training controls, and replacement needs.
S039tier 3
A board-designated capital replacement plan and reserve reflect documented lifecycle needs.
S040tier 1 · critical
Board approves an annual budget and reviews timely budget-to-actual, balance sheet, and cash information.
S041tier 1 · critical
Financial duties are separated or compensating controls are documented for receipts, payments, payroll, and reconciliation.
S042tier 1 · critical
Restricted funds, donations, acknowledgments, and fundraising claims are handled accurately.
S043tier 2 · critical
Board reviews liquidity, revenue concentration, liabilities, and insurance at least annually.
S044tier 2
Independent financial review, compilation, or audit matches legal requirements and organizational risk.
S045tier 3 · critical
Multi-year forecasts and reserves demonstrate a credible continuity plan under adverse scenarios.
S046tier 1 · critical
Every employee has a current job description, lawful classification, payroll processing, and required postings.
S047tier 2 · critical
Employee handbook covers conduct, harassment, leave, hours, safety, complaints, and discipline.
S048tier 2
Performance goals, supervision, compensation review, and annual evaluations are documented.
S049tier 3
Leadership reviews retention, workload, pay equity, development, and single-person dependencies.
S050tier 1 · critical
Volunteer leads have defined roles, authority, contacts, and required safety qualifications.
S051tier 2 · critical
Scheduling, onboarding, supervision, complaints, removal, and escalation are documented.
S052tier 2
Critical responsibilities have backup coverage and transitions do not depend on one volunteer.
S053tier 3
Leadership annually reviews workload, retention, recognition, burnout risk, and succession.
S054tier 1
Organization tracks who participates, what they use, and basic program attendance.
S055tier 1 · critical
Nondiscrimination, accessibility, and inclusion expectations apply to users, staff, volunteers, and programs.
S056tier 2
Measurable annual goals connect activities to participant learning, agency, belonging, or opportunity.
S057tier 2
Participant feedback is collected safely and reviewed with usage and demographic information.
S058tier 2
Instruction supports choice, iteration, peer learning, and multiple ways to participate and demonstrate learning.
S059tier 2
Community partnerships have clear purposes, responsibilities, and mutual benefit.
S060tier 3
A public annual report summarizes finances, participation, outcomes, setbacks, and improvement commitments.
S061tier 3
Evaluation findings demonstrably influence program design, outreach, staffing, and capital decisions.
S084tier 2
Organization participates in peer makerspace networks and shares documentation, curricula, or lessons learned beyond its own walls.
S062tier 1 · critical
Each circulating tool is uniquely identified and checked out to a verified borrower.
S063tier 2 · critical
Loan agreement, due dates, condition, accessories, loss, and safe-use limits are documented.
S064tier 2 · critical
Returned powered tools receive documented functional and electrical inspection before recirculation.
S065tier 3
Loss, damage, late return, utilization, and maintenance trends inform inventory decisions.
S066tier 1 · critical
Parent/guardian consent, emergency contacts, supervision, and authorized pickup rules are enforced.
S067tier 1 · critical
Age and capability rules prohibit or control youth access to hazardous equipment.
S068tier 2 · critical
Background screening and youth-protection training apply to all covered adults before service.
S069tier 2 · critical
Two-adult/open-observable practices, reporting duties, communication, and overnight/transport rules are documented as applicable.
Not yet mapped to any process: S070 — either a real underinvestment or a standard that does not apply here. Each is worth one honest sentence.
S071tier 1 · critical
Commercial use, production limits, shared-tool priority, staging, and prohibited activities are clear.
S072tier 2 · critical
Written agreements define space, term, fees, insurance, IP, milestones, and termination.
S073tier 2 · critical
Production activities receive documented safety, zoning, environmental, and capacity review.
S074tier 3
Program outcomes and community subsidy are measured and reviewed for mission alignment.
S079tier 1 · critical
Every occupant has a written license or rental agreement defining use, fees, term, termination, and non-subletting.
S080tier 1 · critical
Egress, fire access, storage, chemicals, and housekeeping are inspected in private areas.
S082tier 2 · critical
Access, property risk, required insurance, disputes, and abandoned property are addressed in writing.
Not yet mapped to any process: S081 S083 — either a real underinvestment or a standard that does not apply here. Each is worth one honest sentence.
Processes the crosswalk found that no standard reaches — the registry's
contribution back to the framework. Definitions live in STANDARDS_GAPS.md
in the Makerspace-Standards repo.
P1
proposedP2
proposedP3
proposedP4
proposedP5
proposedP6
proposedP7
proposedP8
proposedP9
proposedThe 64 things the registry cannot currently answer about itself, generated from its own weak spots — rows nobody has verified, rows scored broken, rows where a human does every step and nothing is written down. Answer the ones you know. Skipping is the right move on anything you would have to guess at: a guess recorded as fact is worse than a blank.
These used to be documents handed to one person. This is the same interview, open to everyone who runs a process — the difference is that you answer the handful you actually know instead of the whole list.
Sign in to answer. Answers carry your account so a later reader knows who said it; they land in the review queue and get folded into the inventory by hand, never applied automatically.
How this was built, what it was checked against, and — importantly — where it is still weakest. Read this before treating any un-annotated number as fact.
The strategic plan makes this registry's case in its own words — including, in a margin comment, asking for it by name as a precondition for the AI-efficiency work. The plan is a live draft and its numbers are still moving; what is stable, and what the registry builds on, is the set of strategies and their priorities.
Systematize, stabilize […]: website, systems, operations to lay the groundwork for growth, innovation, responsiveness [move past start-up style] Lever strategy #1
Establish and conduct a regular, consistent process for reviewing, improving, and stabilizing operational systems and procedures. Facilities & Operations · Year 1 Strategy #5 · 200 hours budgeted
It might be worth doing an audit of the current systems first to make sure they're actually efficient before bringing in AI. That way, we're improving the process itself, not just automating around any existing issues. Margin comment on the AI-efficiency strategy
80 processes carry the strategy that acts on them — named in plain language: the area of the plan it belongs to, and what that work is meant to do (planning, building, scaling, refining or maintaining). 8 are marked Priority. The registry does not replace the plan: the plan is edited by committees, holds owners and year-by-year sequencing, and should stay where it is.
A strategy is identified here by its name, not by a code. The retired plan
indexed its strategies with objective-and-strategy numbers; the plan now under development has
no identifiers yet, so carrying the old ones would point rows at a document that no longer
governs. The lookup lives in data/strategies.csv; if the plan renames a strategy,
correct it there and every row follows.
What the link buys is two questions neither system answers alone: which processes does this strategy actually touch? and — the more useful one — which of our broken and optimizable processes has no strategy pointed at it at all? The sequence sheet is entirely forward-looking; every one of its states describes work someone intends to do. It has no way to say this is broken right now and nobody is on it, which is what the registry supplies.
The CT Makerspace Network's Standards of Excellence self-assessment defines 68 standards across six domains, scored 0–3 on evidence. Two useful things came of testing the inventory against it.
First, its scale is a better-specified version of ours, so the two should be the same number rather than two competing assessments. A registry that is honestly maintained produces the self-assessment as a by-product — and because its history lives in git, it also produces the three years of dated evidence the "Accredited-ready" level requires.
| Standards of Excellence | Registry | Evidence test |
|---|---|---|
| 0 · Absent | D1 | No reliable practice or evidence |
| 1 · Foundational | D2 | Basic practice exists; informal or inconsistent |
| 2 · Operational | D3 | Written, assigned, consistently implemented, current evidence |
| no equivalent | D4 | Our own test: a second person has worked from the description unaided. The Standards do not ask this separately |
| 3 · Sustained | D5 | Effectiveness reviewed; improvements documented; history supports continuity |
Second, it found eight processes nothing else did — mostly Tier 1 critical safety items that are invisible from code, absent from the strategic plan, and easy to assume someone handles: a chemical inventory and SDS access, lockout/tagout for unsafe equipment, routine shop inspection, hazardous waste disposal, a records-retention and filings calendar, nondiscrimination and accommodation requests, member IP rights, and an admin-access register. All eight enter the inventory as undefined, which is a claim to check rather than a conclusion.
The Profile sheet is mostly unfilled, so conditional modules default to "No" and switch off extensions that plainly apply. Confirmed with JR: tool lending yes; workspace and storage rentals yes, active; incubation already yes; repair partly — Precious Plastic equipment is in place and a larger grant for it was cancelled; youth via partnerships, where the school supervises and MakeHaven is not the custodian.
The one that bites: there is no records-retention policy yet, which is both a Standards requirement in its own right and the blocker on the three-years-of-evidence test for the top recognition level. Applicable standards read 52; corrected, closer to 75.
The bus-factor question — if the person who does this left tomorrow, could someone else pick it up? — is answered by attaching documents to processes. There is already a document doing this job to absorb rather than replace: makehaven.org/operations, which opens "a compilation of key organizational documents… a reference for future staff of the organization" and holds about seventy real policy, procedure and job-description links. 73 rows in the inventory now carry those links under a Docs line.
| The operations doc | The registry |
|---|---|
| A flat list of documents | Documents attached to the process that uses them |
| No dates — cannot tell current from abandoned | A reviewed date per row — 99 of 212 carry one, so the registry's own staleness is visible rather than hidden |
| At least one broken link | Link-checkable in the build |
| Back half is an unrelated field-trip photo log | Scoped to operations |
| Nothing says whether the document is followed | The A, D and I scores say exactly that |
Doing this surfaced two processes no other source did, both rated I5: member conduct & discipline — warning and termination procedures exist as documents, but whether they are followed or logged is unknown — and harassment & conduct complaints, a public policy with an invisible intake. It also sharpens what D1–D5 means: "a current SOP exists" is a claim you can only check against a link.
This is the third attempt at something like this. The previous two were fully specified and never operated — they each needed a new human discipline that nobody sustained. So the maintenance model is the design, not an afterthought: a scheduled review pulls from the systems that already know, proposes updates with evidence, and a human merges them. It works here because most of what happens at MakeHaven leaves a trace somewhere machine-readable.
Two streams feed each review. The first is the digital systems — module history in the website repo, live Stripe and Chargebee state, Drupal webform and badge counts, the KPI dashboards, document edit dates. That is what caught storage billing leaking $565 a month and the acknowledgement email that had never fired across 129 submissions; neither was reported by anyone, both were found by counting. The second is the people — the votes and comments left on this page, exported into one digest that a person reads against the inventory. Neither stream writes to the registry on its own.
Module commit dates and document edit dates can be computed. They do not prove deployment, current documentation or successful use. Process state changes require review of the relevant evidence; the sweep reports candidates and does not edit rows.
"Seven storage assignments still have no subscription — query returns 7 rows." The pass opens a diff citing its evidence and does not merge itself.
Whether something is genuinely tribal, whether an SOP is current rather than merely present, whether a change actually improved anything. Roughly a third of the inventory lives here — the processes with no code behind them at all — and this is the tier the comments on this page exist to answer.
The pass may not create or delete processes, only update fields. Scores move one step per pass. A confidently wrong maintainer is worse than a stale registry, because it launders a guess into a dated commit.
This draft knows what the strategic plan wants those processes to be. It does not know what actually happens. A plan saying "conduct annual drills" is not evidence that drills happen.
Priorities on the Opportunities tab come from the strategic plan's current working
sheet, which is heading for a shorter list. Treat the P1
marks as a demonstration of the mechanism rather than a settled answer; when the plan lands,
data/strategies.csv is replaced and every score moves with it.
The field that would most improve the automation ranking, and the one no codebase can supply. The plan's Year 1 estimates are a forecast, not a measurement.
Lending gets sixteen rows; "space opening and closing" gets one. That reflects where documentation exists, not where the work is.