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. 57 processes run themselves, but only 40 name the module that runs them, and only those can claim the implementation is the description. The other 17 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 218 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 (79) 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. 2 processes are broken — something that is supposed to happen does not, and that is the list to act on first. 22 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. 79 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 24 things wrong when only 2 of them are actually broken.
Everything MakeHaven has decided to work on, in the order we expect to get to it — website fixes, equipment members wished for, and changes to how we run things. It is ranked from what people tell us: how many reported it, the ▲ marks, wish votes, whether it blocks members, money or safety, and how long it has waited. Staff can pin something to the top, and the pin says why.
This is the committed list. The Opportunities tab is different: it ranks processes that could be improved, before anyone has decided to.
Loading…
The 79 processes that are not sitting still — what is mid-change, and what just landed and is still being watched.
10 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?" — 29 actively changing and 50 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 numberfield_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. 2026-09-14 (live, root 68c189cc44): Appointment emails are real calendar invitations (ORGANIZER follows From, RSVP attendees, inline text/calendar part); tool pages carry a "Talk to a facilitator" card with the schedule in a dialog, pre-set to project advice for badge holders. Watching: a real Gmail booking shows Yes/No/Maybe; staff do not misread project-advice bookings as checkouts.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./gems becomes that node, cohort dates on it come from the CiviCRM events whose parent course is GEMS (not from event titles containing "GEMS"), and staff edit the page text on the course node. Watch for: a cohort scheduled without the parent-course link not appearing on /gems; Ashley editing the old (now unpublished) page by habitmakerspace_entrepreneur_dashboard. Member half was invisible (soft-launch block placed in the admin theme); fixed live 08-09. 2026-10-01 staged: the targeted nudge was itself only in the public theme (members get makerspace_gin on /my-membership) and ignored the business goal field (140 checkbox members → 12 businesses). Now placed in the member theme, goal-aware, with a join-the-network step; undismissable banner retired; /list-your-business + staff-picked newsletter products addedmakehaven-venture-linking, not yet deployed)#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.docs/ops/XERO_RECONNECT.md in the site repo. Real outages already email staff once a day and self-recover on cron. 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 2026-10-01: the LLC path's first user, Aaron Monikowski, went two months unpaid. His business-routed bills sat approved but unpaid, and the site never told him Melio had to be set up for the business, because he had been paid personally before. Fixed by hand on live; it was the only stuck payee (131 PAYREQ bills paid Jun–Sep, median 2 days from bill to payment). Staged xero_bills_sync 7d153cd: onboarding judged per pay-to contact, a staff "move open hours to the business" step, and a daily alert for approved bills >7 days past due.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 twice/tasks is the one place for "things people can do": tasks to claim plus opportunities that gather interest ("I'm interested" + note) before staff approve at /tasks/approvals. Recruitment posts to #volunteers; an opportunity short at its decide-by date is declined quietly (email to the interested only); enough interest emails staff@ to approve. Phases 2 (drafts from issue reports) and 3 (tabling type) are planned. Watching: whether opportunities reach approval through interest; kill criteria in the plan.changing. Cancellations now mark the booking's activities Cancelled instead of counting them; the tour KPI excludes Cancelled; a dry-run repair found 129 existing rows (Tour 67, Orientation 48, Meeting 14), applied on live only after JR approves the list. Also staged, switched off: front-door PIN+QR door passes for Calendly tours/walkthroughs/key pickups (event_access_unifi), revoked on cancel. Back to watch once live and the repair has run. A webhook outage lost Apr–Jun records; orientations never backfilled 2026-10-01: the booking works but the record is wrong. The same Calendly→CiviCRM defect as tour booking applies here: cancellations are never applied, and 58 Orientation activities in 12 months belong to cancelled walkthrough/key-pickup bookings. Planned to move onto the home-built scheduler after tours (Phase 3 of the plan), with its own safety-lead badge.workspace_rental 86926b5): licensees now choose personal or business billing on the application and agreement; the website will issue the Xero invoices — refundable deposit + prorated first month on activation, then monthly by the 15th due the 1st — leading with bank transfer, and runs a late-payment ladder (reminder day 3, second notice + staff alert day 7, staff decision day 10, nothing automatic) with a licensee "I'll be late" heads-up. Deposit applied to the final invoice or recorded returned. Rule from JR: always "license", never rent/lease. Existing licenses stay hand-invoiced until migrated. Before it runs: bookkeeper's income + deposits-held account codes, ACH details, first invoice in draft mode. 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.changing. Cancellations now mark the booking's activities Cancelled instead of counting them; the tour KPI excludes Cancelled; a dry-run repair found 129 existing rows (Tour 67, Orientation 48, Meeting 14), applied on live only after JR approves the list. Also staged, switched off: front-door PIN+QR door passes for Calendly tours/walkthroughs/key pickups (event_access_unifi), revoked on cancel. Back to watch once live and the repair has run. 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 needai_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 optimizable → changing 2026-09-30 (staged, not yet on live). Re-measured with answers logged: 27 member questions 08-21→09-30 from ~12 members; the navigator said "I'm not sure" to 8 of 10 (one member types bare tool names and it had no tool list), the tool assistant 7 of 17 plus one invented link; 80 of 864 members had switched every assistant off; the navigator never rendered on profile or badge-catalog pages (members get the other theme there); the staff review console had never been used. Staged fix: both assistants get the full tool and badge index, move to gpt-5.4-mini, strip links to pages that do not exist, the navigator appears on appointments and door-access pages, the hub search box hands unanswered searches to it, and the weekly pulse now reads mh:ai-failed-answers — which is the owner loop this row never had. Watch the "not sure" rate against 56% (navigator 80%) for two pulses after deploystable → watch — the system changed underneath this confirmation. On 2026-09-28 the card system was repointed to the local access box with the website as fallback (fallback export now carries names), and door problems post an @staff alert to #makehaven-facility. JR's 09-10 confirmation predates this. Watching: normal entry, the fallback path when the box is down, and that alerts reach staff. Details in the website repo docs/ops/ACCESS_BOX.md. 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.enabled OFF. The export is armed for four event types; activation is JR's runbook step. Not a running process until then.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 unaidedchanging → watch. Tool channels now hear confirmed status changes only; multi-unit tools code is live and the phase-0 hierarchy cleanup ran on live. Watching: fewer false alarms in tool channels without missing a real outage. The laser/Prusa split still waits on Lior's machine names. Tells members when a tool goes out of service and when it returns; status changes post to Slack automatically. 2026-09-14 (live, root 68c189cc44): Stale-status reminders live: tools past their threshold get a Slack nudge in #broken + the tool channel, weekly until a new log entry; status changes require a reason and can carry an expected-back date; reservations are refused on unusable tools. Watching: first cron posts the V460 laser (132 d) and serger (76 d); Lior clears or confirms both within a week.broken → watch; the row's own exit condition is met. The guards have been on live since 09-24; on test unifi:status prints "writes REFUSED", and the module is uninstalled on dev. The console was seeded on live on 09-30: 855 current members expected, 862 active, nothing left to create, reactivate or provision. Watching: the first member tap at the intercom, and the 8 active console records that are not current door-badged members. Added 2026-09-17 by the cycle review — the add-on's own health was tracked nowhere. The registry is right that UniFi is an add-on to the working ESP32/Home-Assistant door system and that its sync errors are not a door outage, which is why Door access control stays stable. But that judgement also meant nothing watched this, and a runaway ran for three days unseen. unifi_access_sync pushes door-badged Drupal members into the UniFi Access console. Measured on live 2026-09-17: the console holds 23 users while Drupal expects 3,309, so every hourly cron finds ~3,294 "missing", queues a create for each, POSTs it, and logs success — 340,234 watchdog rows in 61 hours, 168,763 of them "created successfully" against a console that gained nothing. Two defects: (1) UnifiSyncManager::reconcile()'s amplification guard tests for exactly zero returned users, so 23 never trips it — it needs a ratio test, and its own docblock describes this failure while testing the wrong predicate; (2) UnifiApiService::createUser() treats any 2xx as success and never reads the response body, which is how this API reports errors. Note the guard did work for the five days the Cloudflare tunnel was down (133 clean aborts, zero amplification) — the outage was masking the bug and restoring the tunnel on 09-14 exposed it.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 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.makerspace_mail_retry) now queues any failed send and retries on a backoff (5 min → 6 h, four tries) before giving up loudly; SMTP keepalive on. A4 not A5: a give-up is a watchdog error, nobody is told unprompted. Watching: a makerspace_mail_retry warning followed by "Retry N delivered" on live is the acceptance; silence proves nothing. Never enable the smtp module's own "send by queue" — it has no runner.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.retired-badges --fix), superseding the "deliberately left active" note below. Discovered 2026-09-17 — the tooling existed and had never been run. A badge whose equipment has left the building keeps appearing in /badges, so members keep passing quizzes for machines we do not have. Retiring one means setting field_badge_inactive on the badge term, which the catalogue view filters on; people who already earned it keep it, because the view members see filters on badge status, not on inactive. drush appointment-facilitator:retired-badges [--fix] finds and fixes them and shipped 2026-08-26, but the first --fix ran on 2026-09-17 at JR's request: Screen Burning, Brother CNC Embroidery and Sewing Machine Heavy Duty Singers retired, four others were already inactive from earlier work. Master Brewer deliberately left active — its HERMs system is Gone but a new brewing machine has arrived that is not on the website at all, and the only operational brewing pages are a grain mill, a fermenting fridge and a community listing. The same release stops badge pages listing Gone tools at all (vinyl 3 → 1, Prusa 15 → 6). Watching: whether anyone runs the report on a cadence, and whether the new brewing machine gets added under Master Brewer.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-76d50bf7bf18field_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 First watch reading 2026-09-14, five days after ship: the page is still unused. Live still shows every 2026 lathe/Bridgeport class with students Registered and no checkout; JR cleared the documentation queue by hand (five of eight rows were class students, approved on the strength of a registration for a past class). Two surfaces had not learned the 09-09 rule and are fixed, staged 2026-09-14, not deployed (release 2026-09-14-badge-docs-queue-and-badges-owed): the documentation queue and its weekly digest now skip class registrants outright, and the Education console gets a "Badges owed after class" tile — 23 classes over 180 days, mostly Build & Badge — with a class-checkout link staff can use on the instructor's behalf. Neither makes the instructor click; the row stays watch on the same three signals, and the untold-Brandon-and-Ken item from 09-09 is still the actual fix. 2026-09-14 (live, root 68c189cc44): Documentation queue and digest skip students of a badge-awarding class; Education console gains a "Badges owed after class" list (180 d). Watching: the queue on live shows only the three non-class rows; Ashley works the owed list once.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 2026-09-14 (live, root 68c189cc44): Attendance is now per session for multi-session classes (instructor_companion_attendance), CiviCRM status derived (present anywhere = Attended). Watching: Sat 09-19 prompt for event 932 session 2, saving it must not change the 09-12 marks.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 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 it 2026-09-14, weekly pulse: the staff Slack notice for a new interest submission is broken. The shared webhook is healthy, but ProposalNotifier overrides the channel to #workshop-proposals, which no longer exists, so Slack answered channel_not_found on the 09-13 submission. The submission still lands in the /admin/education queue; the ping that might get someone to look at it is the part that fails. Logged in AI_ISSUES; the row stays watch because the pipe itself works. 2026-09-14 (live, root 68c189cc44): Instructor profile pages rebuilt and public bios readable (they were hidden from visitors by field permissions), /instructors is a directory of active instructors, Education console has a roster with approve/deactivate, Slack notices for proposals/interest go to a setting (#education-team, unconfirmed with Ashley; both hardcoded channels were dead). Watching: the next real Instructor Interest submission lands in the channel with no channel_not_found.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 2026-09-14 (live, root 68c189cc44): Timesheet now asks which class (field moved up, required for account 6566 with a "Not for a specific class" option, defaults to the latest unpaid class; dashboard Log Hours carries the class). 17 older 2026 requests linked by backfill. Watching: new 6566 requests all carry a class or explicit none; the post-class hub payment step reads Submitted/Paid; complaints about the required pick (event_required_accounts can be emptied without a deploy).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./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 responsesconnect.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. 2026-09-14 (live, root 68c189cc44): Fees can now collect through bank accounts; the lending webhook is not registered in Stripe, so cron reconcilePendingCharges() settles clearing ACH debits and a bounce re-opens the debt. Pre-push review fixed the sweep writing a batch total to every line. Watching: first ACH weekly batch records per-line amounts correctly.reconcile_auto_repair flipped ON (JR approved 09-11); subscriptions now name their payment method and accept bank accounts. Watching: the next weekly reconcile re-creates assignment 330's dead subscription and still reports 119/127 for a person; members with card + bank and no default move to ACH by design.broken → watch. Restock request emails now link the material and its supplier (d85153b; ledger #44552 closed). Watching: the next few real restock emails carry both buttons. Some materials priced at $0.01/unit — any unit cap becomes a silent spend limit. Found broken 2026-09-21 by the weekly pulse: the restock notification email's two Quick Action buttons are wired wrong, and it is a regression from the fix that was meant to repair them. The buttons are rendered by makerspace_material_store_mail_alter(), not by the webform handler. When the material record resolves they are correct; when it does not, the fallback still encodes the pre-2026-08-26 meaning of the form fields — it treats storeitem ("Admin Link") as the link to the material on our site. The 08-26 fix changed the restock link from supplier= to storeitem= so the supplier URL would stop being silently dropped, which means storeitem now carries the supplier URL. So "View material on site" sends staff to the supplier, and "Open supplier page" never renders at all because the field it reads (link_to_item_to_purchase) is empty on every submission ever made. Confirmed rather than inferred: Kate sent a real restock request on 2026-09-17 and reported both symptoms (feedback sid 20552, ledger #44552), and live submissions carry supplier URLs in storeitem (sid 20570 sheffield-pottery, sid 20563 timberwolfblades). Some form of this has been reported since 2026-05-11; this specific shape dates from 08-26. Staff still get the email and can still act, so the process degrades rather than stops — but two of its affordances are wrong or missing. Fix is scoped in docs/DEPLOY_TODO.md; the open question is why material resolution fails on these submissions at all, since that alone would fix the buttons.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. 2026-09-14 (live, root 68c189cc44): Bank accounts are now billable: the resolver takes an explicit default, else a verified bank account, else a card; an ACH debit in processing is treated as in flight (no block, no retry, no failure email) and the webhook or the 20 h stale-invoice sweep settles it. Watching for: the reporting member (uid redacted) adding to tab with no card; the 09-30 month-end run logging bank-account payments and settling within a week; no double debit. Store terms text updated./store/paypal/return confirms the payment, one receipt email per PayPal transaction, and logged-out QR scans go to login instead of "Access denied". Watching: that the first real PayPal Buy Now lands on "Payment received" with a receipt, and whether members still ask staff whether a purchase went through. 2026-09-28: purchase confirmation STAGED (makerspace_material_store 5dabf09). A PayPal buyer used to land on /store/history, which never listed PayPal purchases, so members had to ask staff whether a purchase went through (uid 5380, 09-28). Now: /store/history lists PayPal buys (362 members gain ~2,150 lines), a new /store/paypal/return page confirms once the IPN lands, the member gets a receipt email, and logged-out QR scanners are sent to login instead of a 403. Watch after deploy: the first real PayPal Buy Now reaches "Payment received" and the receipt is in maillog. 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).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 pattern Later the same day, the two follow-ups Kate raised were built rather than promised. Catching what the loop drops is now scripts/feedback-loop-check.py — it searches the whole shipped record for each open item's own node id and submission ids, in four confidence buckets, and runs in the weekly pulse, at deploy close-out and in deploy-status.sh Section 8, which every tab reads before deploying. And both ledger emails were rewritten after staff said they assumed context the reader did not have: the reporter now gets their own words and the date they wrote them plus links to what changed, the page it is about, the item and their own list; staff get See it: and What members are told: per change. A new field_cl_url gives /whats-new entries somewhere to point, and preview: true turns any run into a dry run so wording can be read before it sends.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.makerspace_member_success ed4c5be): every send is bcc'd to staff and the copy now offers help and invites a tour, which is what Kate had been sending by hand. Watching whether the bcc is enough for staff to stop duplicating, and whether member-facing automated email now gets announced when it ships 2026-09-14 (live, root 68c189cc44): The unpaid-lead follow-up now blind-copies Kate and crm@ and invites the applicant to a tour. Watching: the next unpaid lead — copy arrives, no hand-sent duplicate.watch. A cancellation driven by a failed payment is now recorded as nonpayment rather than falling into unknown, and the decline detail is captured on the user. A backfill refiled 35 unknown and 82 unlabelled endings (117 total, identical counts on test and live). Acceptance is explicitly not the backfill: it is the next real dunning cancellation arriving with nonpayment on the profile and a field_payment_failure_code on the user. Until one lands this is shipped, not verified. Watch dblog for "Recorded payment failure detail for user ID" and for the "no decline detail in the payload" line, which would mean Chargebee's payload is not the shape we assumed. Forward-only: 143 unknown endings in the last 24 months carry no raw Chargebee reason at all and are not inferable.Next reviewAt the next membership review; continue the existing nightly reconciliation.AcceptanceThe linked-account revocation check passes with actual scheduled-run evidence. Keep unlinked accounts explicit; a zero-checked run means unavailable, not healthy. Physical door/device caches and other offboarding integrations were not tested in this review.broken → watch. The hourly re-check of settled payment flags (chargebee_fetch_data a3e3e9c) has been on live since 09-29 and runs every hour. A dry run today found the 3 members still flagged all genuinely owe, so none of them is stuck in error. One known defect: linking a new signup to Chargebee threw an error once on 09-29, but the account still ended up linked. Watching: the next payment that lands while a subscription is paused clears on its own, without staff clearing the flag by hand. watch → broken, 2026-09-28 weekly pulse. Recovery does not reliably restore access: a payment_succeeded that arrives while the subscription still reads paused or behind leaves the payment-failed flag set on purpose, and no later event re-checks it. So a member who has paid stays blocked at the door until staff clear the flag by hand (ledger #45601, reported by Kate 2026-09-25). It happened twice in 22 days of log, and a third member was found flagged while paid up and cleared by hand on 2026-09-28. An hourly re-check against Chargebee is staged (chargebee_fetch_data a3e3e9c). 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. 2026-09-14 (live, root 68c189cc44): A Chargebee subscription_cancelled webhook that died on a TypeError (one member, retried eight times) is fixed; term removal had never once succeeded in production. Watching: the next real cancellation runs through to role removal.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/my-purchases, staff view per user) lists everything a member pays MakeHaven for across the separate billing systems: membership dues and storage/workspace from Stripe (Chargebee bills through the same Stripe customer), store tab and PayPal buys, lending fees, and CiviCRM class payments. It degrades to a notice when Stripe cannot be read. Storage was labelled by the generated unit name until a same-day hotfix. Watching: members with no Stripe customer ID (see Stripe customer linkage) see an incomplete page.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./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./makerspace-dashboard (anonymous holds view makerspace dashboard) renders the finance, retention, education, DEI and operations tabs from ~60 chart builders. Broken right now: the Education tab has returned HTTP 500 to everyone since 2026-09-09 18:47, when the "chart correctness review" commit (1dc5c6a) shipped a query that joins a badge-access table on the req alias five lines before req is joined. A one-line reorder; found five days later by the pulse's watchdog read rather than by anyone looking at the page, which is why A is 4 and not 5. Also noted the same day: the DEI location map geocodes live on every request and crawlers push it into Nominatim's rate limit, so the map renders empty. Both logged in the site repo's AI_ISSUES 2026-09-14 (live, root 68c189cc44): Education tab fixed (join order), 200 signed out, KPIs warmed. Watching: no new 500s on /makerspace-dashboard/* for a week.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.136 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.
136 of 218 processes are here; 82 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. 81 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
FLIP: lines guard that), that the 544-row backlog starts moving now the queue can suggest people, and that the first real credit reaches Chargebee. Nothing is delivered until the first referrer is thanked. 2026-09-18: the mechanism is now on live, deliberately switched OFF. The programme shipped — invitations, a flat $50 credit to the referrer on the referred member's first successful charge, the REFERRED coupon, a staff review console at /admin/people/referrals/awards, and referral charts on the Outreach dashboard. awards_enabled and thanks_enabled are both false on live, so nothing runs yet and members see no change; the row stays changing rather than moving toward stable for exactly that reason. The by-hand process described above is still the live one. Two things must happen before the flip: the Chargebee hosted-page link and start date set, and a staff address configured so unmatched referrals reach somebody. /member-referral-program still tells members to name a referrer by typing, which the picker replaces — update it at flip time. Tracked under Pending flips in DEPLOY_TODO; the member-facing announcement belongs to the flip, not the deploy. 2026-09-22: the first self-pick landed and was misread. A new member picked a referrer from the signup list; the queue showed it as "Identity confirmed" with a blank reviewer, and Kate asked whether an AI was now confirming referrals. Nobody was told the picker auto-confirms, and no screen said so. JR kept the rule (self-pick stays auto-confirmed, no staff gate); the wording fix is staged in makerspace_referrals 3a371fe — "Identity matched" for a member's own pick vs "Identity confirmed" for a staff decision (Kate's terms), plus a Decided-by column. Still changing; switches still off.
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 First watch reading 2026-09-14, five days after ship: the page is still unused. Live still shows every 2026 lathe/Bridgeport class with students Registered and no checkout; JR cleared the documentation queue by hand (five of eight rows were class students, approved on the strength of a registration for a past class). Two surfaces had not learned the 09-09 rule and are fixed, staged 2026-09-14, not deployed (release 2026-09-14-badge-docs-queue-and-badges-owed): the documentation queue and its weekly digest now skip class registrants outright, and the Education console gets a "Badges owed after class" tile — 23 classes over 180 days, mostly Build & Badge — with a class-checkout link staff can use on the instructor's behalf. Neither makes the instructor click; the row stays watch on the same three signals, and the untold-Brandon-and-Ken item from 09-09 is still the actual fix. 2026-09-14 (live, root 68c189cc44): Documentation queue and digest skip students of a badge-awarding class; Education console gains a "Badges owed after class" list (180 d). Watching: the queue on live shows only the three non-class rows; Ashley works the owed list once.
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 for unclaimed payments (staged 2026-10-01: a daily alert for approved bills nobody has paid, which is the other half; a Melio-unclaimed payment still reads PAID in Xero and stays invisible) — 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
broken. The lookup routes now record every caller and refuse none; device keys and the patches that keep a refused key from denying a door are ready but not deployed. Enforcement is a separate, deliberate switch once the caller audit shows no real device would be refused. 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
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
/gems becomes that node, cohort dates on it come from the CiviCRM events whose parent course is GEMS (not from event titles containing "GEMS"), and staff edit the page text on the course node. Watch for: a cohort scheduled without the parent-course link not appearing on /gems; Ashley editing the old (now unpublished) page by habit
Education & InstructionP2Grow
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
broken → watch. New accounts are matched to their Chargebee payment automatically (six-hourly sweep + on account creation), existing account holders are sent to log in to finish instead of a dead end, returning members on a cancelled billing record are relinked, and the paid hand-off page hides menus and the Google button. The
Membership & BillingP2RefineDocsNew Member procedure · Membership Agreement
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 2026-09-14 (live, root 68c189cc44): Attendance is now per session for multi-session classes (instructor_companion_attendance), CiviCRM status derived (present anywhere = Attended). Watching: Sat 09-19 prompt for event 932 session 2, saving it must not change the 09-12 marks.
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.
workspace_rental 86926b5): licensees now choose personal or business billing on the application and agreement; the website will issue the Xero invoices — refundable deposit + prorated first month on activation, then monthly by the 15th due the 1st — leading with bank transfer, and runs a late-payment ladder (reminder day 3, second notice + staff alert day 7, staff decision day 10, nothing automatic) with a licensee "I'll be late" heads-up. Deposit applied to the final invoice or recorded returned. Rule from JR: always "license", never rent/lease. Existing licenses stay hand-invoiced until migrated. Before it runs: bookkeeper's income + deposits-held account codes, ACH details, first invoice in draft mode. 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.
Membership & BillingDocsWorkspaces
broken → watch. The hourly re-check of settled payment flags (chargebee_fetch_data a3e3e9c) has been on live since 09-29 and runs every hour. A dry run today found the 3 members still flagged all genuinely owe, so none of them is stuck in error. One known defect: linking a new signup to Chargebee threw an error once on 09-29, but the account still ended up linked. Watching: the next payment that lands while a subscription is paused clears on its own, without staff clearing the flag by hand. watch → broken, 2026-09-28 weekly pulse. Recovery does not reliably restore access: a payment_succeeded that arrives while the subscription still reads paused or behind leaves the payment-failed flag set on purpose, and no later event re-checks it. So a member who has paid stays blocked at the door until staff clear the flag by hand (ledger #45601, reported by Kate 2026-09-25). It happened twice in 22 days of log, and a third member was found flagged while paid up and cleared by hand on 2026-09-28. An hourly re-check against Chargebee is staged (chargebee_fetch_data a3e3e9c). 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. 2026-09-14 (live, root 68c189cc44): A Chargebee subscription_cancelled webhook that died on a TypeError (one member, retried eight times) is fixed; term removal had never once succeeded in production. Watching: the next real cancellation runs through to role removal.
Membership & Billing
reconcile_auto_repair flipped ON (JR approved 09-11); subscriptions now name their payment method and accept bank accounts. Watching: the next weekly reconcile re-creates assignment 330's dead subscription and still reports 119/127 for a person; members with card + bank and no default move to ACH by design.
Lending, Storage & Store
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)
changing. Cancellations now mark the booking's activities Cancelled instead of counting them; the tour KPI excludes Cancelled; a dry-run repair found 129 existing rows (Tour 67, Orientation 48, Meeting 14), applied on live only after JR approves the list. Also staged, switched off: front-door PIN+QR door passes for Calendly tours/walkthroughs/key pickups (event_access_unifi), revoked on cancel. Back to watch once live and the repair has run. 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
Outreach & RecruitmentDocsTour scheduling plan · Tour booking
/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 2026-09-14, weekly pulse: the staff Slack notice for a new interest submission is broken. The shared webhook is healthy, but ProposalNotifier overrides the channel to #workshop-proposals, which no longer exists, so Slack answered channel_not_found on the 09-13 submission. The submission still lands in the /admin/education queue; the ping that might get someone to look at it is the part that fails. Logged in AI_ISSUES; the row stays watch because the pipe itself works. 2026-09-14 (live, root 68c189cc44): Instructor profile pages rebuilt and public bios readable (they were hidden from visitors by field permissions), /instructors is a directory of active instructors, Education console has a roster with approve/deactivate, Slack notices for proposals/interest go to a setting (#education-team, unconfirmed with Ashley; both hardcoded channels were dead). Watching: the next real Instructor Interest submission lands in the channel with no channel_not_found.
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 Later the same day, the two follow-ups Kate raised were built rather than promised. Catching what the loop drops is now scripts/feedback-loop-check.py — it searches the whole shipped record for each open item's own node id and submission ids, in four confidence buckets, and runs in the weekly pulse, at deploy close-out and in deploy-status.sh Section 8, which every tab reads before deploying. And both ledger emails were rewritten after staff said they assumed context the reader did not have: the reporter now gets their own words and the date they wrote them plus links to what changed, the page it is about, the item and their own list; staff get See it: and What members are told: per change. A new field_cl_url gives /whats-new entries somewhere to point, and preview: true turns any run into a dry run so wording can be read before it sends.
Member Experience & RetentionP2Refine
makerspace_member_success ed4c5be): every send is bcc'd to staff and the copy now offers help and invites a tour, which is what Kate had been sending by hand. Watching whether the bcc is enough for staff to stop duplicating, and whether member-facing automated email now gets announced when it ships 2026-09-14 (live, root 68c189cc44): The unpaid-lead follow-up now blind-copies Kate and crm@ and invites the applicant to a tour. Watching: the next unpaid lead — copy arrives, no hand-sent duplicate.
Member Experience & RetentionP1Refine
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
changing. Cancellations now mark the booking's activities Cancelled instead of counting them; the tour KPI excludes Cancelled; a dry-run repair found 129 existing rows (Tour 67, Orientation 48, Meeting 14), applied on live only after JR approves the list. Also staged, switched off: front-door PIN+QR door passes for Calendly tours/walkthroughs/key pickups (event_access_unifi), revoked on cancel. Back to watch once live and the repair has run. A webhook outage lost Apr–Jun records; orientations never backfilled 2026-10-01: the booking works but the record is wrong. The same Calendly→CiviCRM defect as tour booking applies here: cancellations are never applied, and 58 Orientation activities in 12 months belong to cancelled walkthrough/key-pickup bookings. Planned to move onto the home-built scheduler after tours (Phase 3 of the plan), with its own safety-lead badge.
Member Experience & RetentionDocsTour scheduling plan · Orientation video / process
/store/paypal/return confirms the payment, one receipt email per PayPal transaction, and logged-out QR scans go to login instead of "Access denied". Watching: that the first real PayPal Buy Now lands on "Payment received" with a receipt, and whether members still ask staff whether a purchase went through. 2026-09-28: purchase confirmation STAGED (makerspace_material_store 5dabf09). A PayPal buyer used to land on /store/history, which never listed PayPal purchases, so members had to ask staff whether a purchase went through (uid 5380, 09-28). Now: /store/history lists PayPal buys (362 members gain ~2,150 lines), a new /store/paypal/return page confirms once the IPN lands, the member gets a receipt email, and logged-out QR scanners are sent to login instead of a 403. Watch after deploy: the first real PayPal Buy Now reaches "Payment received" and the receipt is in maillog. 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).
Lending, Storage & Store
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
The ranking above is arithmetic — impact multiplied by how manual something still is. It cannot know what staff have noticed. These 9 were flagged by a person, and that is a different and often better signal.
watch rather than stable: nobody has walked the interactive matrix by hand — missing prerequisite refused, basic-pending promoting advanced to pending, sequential approval across the class, inline and on-request paths. That verification stays open in DEPLOY_TODO; shipping it is not the same as having watched it work.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. 29 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: 56 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 218 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 2026-09-14, weekly pulse: the staff Slack notice for a new interest submission is broken. The shared webhook is healthy, but ProposalNotifier overrides the channel to #workshop-proposals, which no longer exists, so Slack answered channel_not_found on the 09-13 submission. The submission still lands in the /admin/education queue; the ping that might get someone to look at it is the part that fails. Logged in AI_ISSUES; the row stays watch because the pipe itself works. 2026-09-14 (live, root 68c189cc44): Instructor profile pages rebuilt and public bios readable (they were hidden from visitors by field permissions), /instructors is a directory of active instructors, Education console has a roster with approve/deactivate, Slack notices for proposals/interest go to a setting (#education-team, unconfirmed with Ashley; both hardcoded channels were dead). Watching: the next real Instructor Interest submission lands in the channel with no channel_not_found. |
| 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 | 3 | 3 | changing | 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 2026-09-14 (live, root 68c189cc44): Attendance is now per session for multi-session classes (instructor_companion_attendance), CiviCRM status derived (present anywhere = Attended). Watching: Sat 09-19 prompt for event 932 session 2, saving it must not change the 09-12 marks. |
| 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 | watch | 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 2026-09-14 (live, root 68c189cc44): Timesheet now asks which class (field moved up, required for account 6566 with a "Not for a specific class" option, defaults to the latest unpaid class; dashboard Log Hours carries the class). 17 older 2026 requests linked by backfill. Watching: new 6566 requests all carry a class or explicit none; the post-class hub payment step reads Submitted/Paid; complaints about the required pick (event_required_accounts can be emptied without a deploy). |
| GEMS cohort management | 2 | 2 | 3 | changing | Running now, with details in the event listing. Changing 2026-09-12 (staged, not yet live): the public GEMS page moves onto the GEMS course node — /gems becomes that node, cohort dates on it come from the CiviCRM events whose parent course is GEMS (not from event titles containing "GEMS"), and staff edit the page text on the course node. Watch for: a cohort scheduled without the parent-course link not appearing on /gems; Ashley editing the old (now unpublished) page by habit |
| 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 | watch | 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. SHIPPED LIVE 2026-09-10. The content step applied all four definitions on test and then live, and re-previews as already-applied on both; announced as changelog #45126. Watching: that a member can be approved basic-then-advanced inside one visit, that premature awards are refused, and that no already-earned badge was disturbed. Not yet done, and the reason this is watch rather than stable: nobody has walked the interactive matrix by hand — missing prerequisite refused, basic-pending promoting advanced to pending, sequential approval across the class, inline and on-request paths. That verification stays open in DEPLOY_TODO; shipping it is not the same as having watched it work. |
| Badge checkout appointment | 4 | 3 | 4 | changing | 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. 2026-09-14 (live, root 68c189cc44): Appointment emails are real calendar invitations (ORGANIZER follows From, RSVP attendees, inline text/calendar part); tool pages carry a "Talk to a facilitator" card with the schedule in a dialog, pre-set to project advice for badge holders. Watching: a real Gmail booking shows Yes/No/Maybe; staff do not misread project-advice bookings as checkouts.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. **2026-09-15, staged not deployed (release 2026-09-15-badge-grid-full-shifts-and-slot-label-timezone): a member clicked a shift's "Schedule 6:00pm" button while the early slots were taken, the form quietly pre-selected the first free pair two hours later, and they saved in eight seconds — then booked a second facilitator for the same hour. The badge/tool grid now hides a shift that has no free run of slots long enough for the badge's checkout, and the booking form's slot-time labels are placed in the site timezone instead of the visitor's device zone (a non-Eastern phone had shown every label shifted by its offset). Watching after ship: "it moved my time" reports should stop; a shift with three bookings must vanish from the grid while one with two still shows. — 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 First watch reading 2026-09-14, five days after ship: the page is still unused. Live still shows every 2026 lathe/Bridgeport class with students Registered and no checkout; JR cleared the documentation queue by hand (five of eight rows were class students, approved on the strength of a registration for a past class). Two surfaces had not learned the 09-09 rule and are fixed, staged 2026-09-14, not deployed (release 2026-09-14-badge-docs-queue-and-badges-owed): the documentation queue and its weekly digest now skip class registrants outright, and the Education console gets a "Badges owed after class" tile — 23 classes over 180 days, mostly Build & Badge — with a class-checkout link staff can use on the instructor's behalf. Neither makes the instructor click; the row stays watch on the same three signals, and the untold-Brandon-and-Ken item from 09-09 is still the actual fix. 2026-09-14 (live, root 68c189cc44): Documentation queue and digest skip students of a badge-awarding class; Education console gains a "Badges owed after class" list (180 d). Watching: the queue on live shows only the three non-class rows; Ashley works the owed list once. |
| 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 catalogue upkeep | 3 | 2 | 2 | watch | 2026-09-28: JR retired Master Brewer on live (retired-badges --fix), superseding the "deliberately left active" note below. Discovered 2026-09-17 — the tooling existed and had never been run. A badge whose equipment has left the building keeps appearing in /badges, so members keep passing quizzes for machines we do not have. Retiring one means setting field_badge_inactive on the badge term, which the catalogue view filters on; people who already earned it keep it, because the view members see filters on badge status, not on inactive. drush appointment-facilitator:retired-badges [--fix] finds and fixes them and shipped 2026-08-26, but the first --fix ran on 2026-09-17 at JR's request: Screen Burning, Brother CNC Embroidery and Sewing Machine Heavy Duty Singers retired, four others were already inactive from earlier work. Master Brewer deliberately left active — its HERMs system is Gone but a new brewing machine has arrived that is not on the website at all, and the only operational brewing pages are a grain mill, a fermenting fridge and a community listing. The same release stops badge pages listing Gone tools at all (vinyl 3 → 1, Prusa 15 → 6). Watching: whether anyone runs the report on a cadence, and whether the new brewing machine gets added under Master Brewer. |
| 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 | changing | makerspace_entrepreneur_dashboard. Member half was invisible (soft-launch block placed in the admin theme); fixed live 08-09. 2026-10-01 staged: the targeted nudge was itself only in the public theme (members get makerspace_gin on /my-membership) and ignored the business goal field (140 checkbox members → 12 businesses). Now placed in the member theme, goal-aware, with a join-the-network step; undismissable banner retired; /list-your-business + staff-picked newsletter products added |
| 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 | changing | 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. 2026-10-01: 2 businesses pushed, 0 members consented, and a pushed venture was invisible to partners (no founder link) and duplicated on Nexus sign-in. Staged a fix on both sides: consent-first push with founder_ref, and owner-scoped SSO refs (Nexus branch makehaven-venture-linking, not yet deployed) |
| 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 | 2026-09-29: the volunteer board SHIPPED LIVE on top of /tasks. Opportunities can gather interest before staff approve; claims idle 14/28 days email their volunteer (only claims made after launch); staff see older claims as a cleanup list at /tasks/approvals. Member suggestions now enter "gathering" rather than a silent queue. See the new "Volunteer opportunity board" row. 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 | watch | 2026-09-29: stable → watch — the system changed underneath this confirmation. On 2026-09-28 the card system was repointed to the local access box with the website as fallback (fallback export now carries names), and door problems post an @staff alert to #makehaven-facility. JR's 09-10 confirmation predates this. Watching: normal entry, the fallback path when the box is down, and that alerts reach staff. Details in the website repo docs/ops/ACCESS_BOX.md. 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. |
| UniFi Access member sync (add-on) | 3 | 3 | 2 | watch | 2026-10-01 — broken → watch; the row's own exit condition is met. The guards have been on live since 09-24; on test unifi:status prints "writes REFUSED", and the module is uninstalled on dev. The console was seeded on live on 09-30: 855 current members expected, 862 active, nothing left to create, reactivate or provision. Watching: the first member tap at the intercom, and the 8 active console records that are not current door-badged members. Added 2026-09-17 by the cycle review — the add-on's own health was tracked nowhere. The registry is right that UniFi is an add-on to the working ESP32/Home-Assistant door system and that its sync errors are not a door outage, which is why Door access control stays stable. But that judgement also meant nothing watched this, and a runaway ran for three days unseen. unifi_access_sync pushes door-badged Drupal members into the UniFi Access console. Measured on live 2026-09-17: the console holds 23 users while Drupal expects 3,309, so every hourly cron finds ~3,294 "missing", queues a create for each, POSTs it, and logs success — 340,234 watchdog rows in 61 hours, 168,763 of them "created successfully" against a console that gained nothing. Two defects: (1) UnifiSyncManager::reconcile()'s amplification guard tests for exactly zero returned users, so 23 never trips it — it needs a ratio test, and its own docblock describes this failure while testing the wrong predicate; (2) UnifiApiService::createUser() treats any 2xx as success and never reads the response body, which is how this API reports errors. Note the guard did work for the five days the Cloudflare tunnel was down (133 clean aborts, zero amplification) — the outage was masking the bug and restoring the tunnel on 09-14 exposed it. |
| Event visitor passes | 4 | 3 | 2 | watch | 2026-09-29: running. JR switched passes ON on live 2026-09-28 after a door test; registrants now get an email 4 h before with a door code + QR that opens from 1 h before the start. The branded pass email shipped 09-29. Registrants from before 09-28 have no pass (backfill is JR's call). Watching: the first real sends (09-30 class onward) arrive, open the door, and cancellations are refused. QR / PIN for registrants, via the UniFi add-on 2026-09-14 (live, root 68c189cc44): Correction: the module had never run on live (it guarded on a Drupal 7 function; zero passes ever). Rewritten against the real UniFi Access API, verified at the door on 09-14 (PIN + QR opened, cancel refused), deployed with enabled OFF. The export is armed for four event types; activation is JR's runbook step. Not a running process until then. |
| 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 | watch | 2026-09-29: SHIPPED LIVE; changing → watch. Tool channels now hear confirmed status changes only; multi-unit tools code is live and the phase-0 hierarchy cleanup ran on live. Watching: fewer false alarms in tool channels without missing a real outage. The laser/Prusa split still waits on Lior's machine names. Tells members when a tool goes out of service and when it returns; status changes post to Slack automatically. 2026-09-14 (live, root 68c189cc44): Stale-status reminders live: tools past their threshold get a Slack nudge in #broken + the tool channel, weekly until a new log entry; status changes require a reason and can carry an expected-back date; reservations are refused on unusable tools. Watching: first cron posts the V460 laser (132 d) and serger (76 d); Lior clears or confirms both within a week.Description inferred 2026-08-15 — not confirmed by anyone who runs it |
| API endpoint security | 3 | 3 | 5 | broken | 2026-10-01 — fix staged in report mode, still broken. The lookup routes now record every caller and refuse none; device keys and the patches that keep a refused key from denying a door are ready but not deployed. Enforcement is a separate, deliberate switch once the caller audit shows no real device would be refused. 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. 2026-09-14 (live, root 68c189cc44): Fees can now collect through bank accounts; the lending webhook is not registered in Stripe, so cron reconcilePendingCharges() settles clearing ACH debits and a bounce re-opens the debt. Pre-push review fixed the sweep writing a batch total to every line. Watching: first ACH weekly batch records per-line amounts correctly. |
| 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. 2026-09-14 (live, root 68c189cc44): reconcile_auto_repair flipped ON (JR approved 09-11); subscriptions now name their payment method and accept bank accounts. Watching: the next weekly reconcile re-creates assignment 330's dead subscription and still reports 119/127 for a person; members with card + bank and no default move to ACH by design. |
| Store purchase | 3 | 2 | 3 | watch | 2026-09-29: purchase confirmation SHIPPED LIVE. /store/history now lists PayPal buys, /store/paypal/return confirms the payment, one receipt email per PayPal transaction, and logged-out QR scans go to login instead of "Access denied". Watching: that the first real PayPal Buy Now lands on "Payment received" with a receipt, and whether members still ask staff whether a purchase went through. 2026-09-28: purchase confirmation STAGED (makerspace_material_store 5dabf09). A PayPal buyer used to land on /store/history, which never listed PayPal purchases, so members had to ask staff whether a purchase went through (uid 5380, 09-28). Now: /store/history lists PayPal buys (362 members gain ~2,150 lines), a new /store/paypal/return page confirms once the IPN lands, the member gets a receipt email, and logged-out QR scanners are sent to login instead of a 403. Watch after deploy: the first real PayPal Buy Now reaches "Payment received" and the receipt is in maillog. 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 | 2026-09-29 SHIPPED LIVE: a double tap on Pay can no longer charge twice (one charge per member at a time), a declined card names the other saved card, and PayPal tab payments clear the right line. 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. 2026-09-14 (live, root 68c189cc44): Bank accounts are now billable: the resolver takes an explicit default, else a verified bank account, else a card; an ACH debit in processing is treated as in flight (no block, no retry, no failure email) and the webhook or the 20 h stale-invoice sweep settles it. Watching for: the reporting member (uid redacted) adding to tab with no card; the 09-30 month-end run logging bank-account payments and settling within a week; no double debit. Store terms text updated. |
| Store inventory restock & reorder | 3 | 2 | 2 | watch | 2026-09-29: button fix SHIPPED LIVE; broken → watch. Restock request emails now link the material and its supplier (d85153b; ledger #44552 closed). Watching: the next few real restock emails carry both buttons. Some materials priced at $0.01/unit — any unit cap becomes a silent spend limit. Found broken 2026-09-21 by the weekly pulse: the restock notification email's two Quick Action buttons are wired wrong, and it is a regression from the fix that was meant to repair them. The buttons are rendered by makerspace_material_store_mail_alter(), not by the webform handler. When the material record resolves they are correct; when it does not, the fallback still encodes the pre-2026-08-26 meaning of the form fields — it treats storeitem ("Admin Link") as the link to the material on our site. The 08-26 fix changed the restock link from supplier= to storeitem= so the supplier URL would stop being silently dropped, which means storeitem now carries the supplier URL. So "View material on site" sends staff to the supplier, and "Open supplier page" never renders at all because the field it reads (link_to_item_to_purchase) is empty on every submission ever made. Confirmed rather than inferred: Kate sent a real restock request on 2026-09-17 and reported both symptoms (feedback sid 20552, ledger #44552), and live submissions carry supplier URLs in storeitem (sid 20570 sheffield-pottery, sid 20563 timberwolfblades). Some form of this has been reported since 2026-05-11; this specific shape dates from 08-26. Staff still get the email and can still act, so the process degrades rather than stops — but two of its affordances are wrong or missing. Fix is scoped in docs/DEPLOY_TODO.md; the open question is why material resolution fails on these submissions at all, since that alone would fix the buttons. |
| 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 | 2026-10-01 — broken → watch. The hourly re-check of settled payment flags (chargebee_fetch_data a3e3e9c) has been on live since 09-29 and runs every hour. A dry run today found the 3 members still flagged all genuinely owe, so none of them is stuck in error. One known defect: linking a new signup to Chargebee threw an error once on 09-29, but the account still ended up linked. Watching: the next payment that lands while a subscription is paused clears on its own, without staff clearing the flag by hand. watch → broken, 2026-09-28 weekly pulse. Recovery does not reliably restore access: a payment_succeeded that arrives while the subscription still reads paused or behind leaves the payment-failed flag set on purpose, and no later event re-checks it. So a member who has paid stays blocked at the door until staff clear the flag by hand (ledger #45601, reported by Kate 2026-09-25). It happened twice in 22 days of log, and a third member was found flagged while paid up and cleared by hand on 2026-09-28. An hourly re-check against Chargebee is staged (chargebee_fetch_data a3e3e9c). 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. 2026-09-14 (live, root 68c189cc44): A Chargebee subscription_cancelled webhook that died on a TypeError (one member, retried eight times) is fixed; term removal had never once succeeded in production. Watching: the next real cancellation runs through to role removal.Description inferred 2026-08-20 — not confirmed by anyone who runs it |
| Cancellation & offboarding | 4 | 3 | 4 | watch | Current2026-09-10 Live audit: 850 linked accounts checked, zero lapsed with membership roles. Automatic reconciliation is enabled; 13 scheduled runs since activation include three later exceptions revoked. The four historical accounts no longer hold membership roles or appear in the cached fallback export.Next actionNext membership review: establish the disposition of two accounts without Chargebee links. They are excluded from the audit, not proven unpaid. Record any linkage or exception decision in the existing staff workflow. 2026-09-10 — how a cancellation is filed changed, which is why this moves back to watch. A cancellation driven by a failed payment is now recorded as nonpayment rather than falling into unknown, and the decline detail is captured on the user. A backfill refiled 35 unknown and 82 unlabelled endings (117 total, identical counts on test and live). Acceptance is explicitly not the backfill: it is the next real dunning cancellation arriving with nonpayment on the profile and a field_payment_failure_code on the user. Until one lands this is shipped, not verified. Watch dblog for "Recorded payment failure detail for user ID" and for the "no decline detail in the payload" line, which would mean Chargebee's payload is not the shape we assumed. Forward-only: 143 unknown endings in the last 24 months carry no raw Chargebee reason at all and are not inferable.Next reviewAt the next membership review; continue the existing nightly reconciliation.AcceptanceThe linked-account revocation check passes with actual scheduled-run evidence. Keep unlinked accounts explicit; a zero-checked run means unavailable, not healthy. Physical door/device caches and other offboarding integrations were not tested in this review. |
| 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 |
| Member payment history (My purchases) | 4 | 3 | 3 | watch | Added 2026-09-29 — shipped live that day. One page (/my-purchases, staff view per user) lists everything a member pays MakeHaven for across the separate billing systems: membership dues and storage/workspace from Stripe (Chargebee bills through the same Stripe customer), store tab and PayPal buys, lending fees, and CiviCRM class payments. It degrades to a notice when Stripe cannot be read. Storage was labelled by the generated unit name until a same-day hotfix. Watching: members with no Stripe customer ID (see Stripe customer linkage) see an incomplete page. |
| 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 | changing | 2026-10-01 STAGED, ships switched off (workspace_rental 86926b5): licensees now choose personal or business billing on the application and agreement; the website will issue the Xero invoices — refundable deposit + prorated first month on activation, then monthly by the 15th due the 1st — leading with bank transfer, and runs a late-payment ladder (reminder day 3, second notice + staff alert day 7, staff decision day 10, nothing automatic) with a licensee "I'll be late" heads-up. Deposit applied to the final invoice or recorded returned. Rule from JR: always "license", never rent/lease. Existing licenses stay hand-invoiced until migrated. Before it runs: bookkeeper's income + deposits-held account codes, ACH details, first invoice in draft mode. 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. 2026-09-11: the automation runs and nobody told the people it overlaps with. The pre-account half — the "Finish joining MakeHaven" email to someone who filled the join form and never paid — has been sending continuously since 08-21 (23 sends by 09-11, most recent that morning) after an earlier period switched off, but it was never announced, so Kate was following the same applicants up by hand and applicants received two emails for roughly three weeks. She found out by reading a release email. This is the A4/A5 line in practice: the send is unattended and nothing about it reaches a human. Fix staged 09-11 (makerspace_member_success ed4c5be): every send is bcc'd to staff and the copy now offers help and invites a tour, which is what Kate had been sending by hand. Watching whether the bcc is enough for staff to stop duplicating, and whether member-facing automated email now gets announced when it ships 2026-09-14 (live, root 68c189cc44): The unpaid-lead follow-up now blind-copies Kate and crm@ and invites the applicant to a tour. Watching: the next unpaid lead — copy arrives, no hand-sent duplicate.RaisedKate Cebik, 2026-09-11 — staff duplicating an automated member email nobody knew was running |
| Orientation scheduling | 3 | 2 | 3 | changing | Calendly. 2026-10-05: fix STAGED (not yet live), state → changing. Cancellations now mark the booking's activities Cancelled instead of counting them; the tour KPI excludes Cancelled; a dry-run repair found 129 existing rows (Tour 67, Orientation 48, Meeting 14), applied on live only after JR approves the list. Also staged, switched off: front-door PIN+QR door passes for Calendly tours/walkthroughs/key pickups (event_access_unifi), revoked on cancel. Back to watch once live and the repair has run. A webhook outage lost Apr–Jun records; orientations never backfilled 2026-10-01: the booking works but the record is wrong. The same Calendly→CiviCRM defect as tour booking applies here: cancellations are never applied, and 58 Orientation activities in 12 months belong to cancelled walkthrough/key-pickup bookings. Planned to move onto the home-built scheduler after tours (Phase 3 of the plan), with its own safety-lead badge. |
| 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 Later the same day, the two follow-ups Kate raised were built rather than promised. Catching what the loop drops is now scripts/feedback-loop-check.py — it searches the whole shipped record for each open item's own node id and submission ids, in four confidence buckets, and runs in the weekly pulse, at deploy close-out and in deploy-status.sh Section 8, which every tab reads before deploying. And both ledger emails were rewritten after staff said they assumed context the reader did not have: the reporter now gets their own words and the date they wrote them plus links to what changed, the page it is about, the item and their own list; staff get See it: and What members are told: per change. A new field_cl_url gives /whats-new entries somewhere to point, and preview: true turns any run into a dry run so wording can be read before it sends. |
| 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 | changing | 2026-10-05: fix STAGED (not yet live), state → changing. Cancellations now mark the booking's activities Cancelled instead of counting them; the tour KPI excludes Cancelled; a dry-run repair found 129 existing rows (Tour 67, Orientation 48, Meeting 14), applied on live only after JR approves the list. Also staged, switched off: front-door PIN+QR door passes for Calendly tours/walkthroughs/key pickups (event_access_unifi), revoked on cancel. Back to watch once live and the repair has run. 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 | changing | Current2026-09-16: Live has 546 named main profiles, 132 since 2025, 18 member-discovery answers without a name, and zero staff review decisions. This corrects the historical 173/$8,650 estimate above; names do not establish unpaid awards. The attribution queue is Live; capture recovery, acknowledgement and pending-worklist improvements are staged, not deployed. Browser review the same day found and fixed a second placement defect in that staged work: a fractional render weight cannot interleave with radio options, because Element::children() sorts on floor(weight x 1000), so the name field rendered above every option instead of after the referral choice. The earlier check passed it by bounding only the distance between field and option, not the direction. Credit history remains unverified; JR believes it is in Chargebee and will confirm with Kate.Next actionBundled Test acceptance and reconciliation of manual awards before financial automation.Next reviewTest review and confirmation of manual-credit evidenceAcceptanceStaff review tests pass (5 kernel / 66 assertions; 1 functional / 30 assertions). Both member capture and staff queue now have passing local browser journeys, the staff one covering queue findability, the Needs review filter and count, decisions leaving and reopening the worklist, and anonymous denial.RaisedJR 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 | changing | 2026-10-01: moving onto the volunteer board (STAGED). Kate's tabling Google Doc (/table) becomes a "Tabling" opportunity type with event link, RSVP-by (sign-ups close the day before), organizer contact and our status with them (staff-only), fee, selling allowed, what to bring and promo code; slots per shift; a summary posts to the outreach committee channel. The doc stays until Kate has used the board for a few events. 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. |
| Transactional email delivery | 4 | 3 | 3 | watch | Added 2026-09-14 by the deploy tab — nothing had a row for Drupal's outgoing operational mail (booking confirmations, tool-due reminders, appointment invites, onboarding follow-ups) as a process that can fail. It had been failing: about 5% of sends since 2026-09-02 died on a Postmark connection error and were lost for good, because the smtp module logs and returns. 2026-09-14 (live, root 68c189cc44): a retry wrapper (makerspace_mail_retry) now queues any failed send and retries on a backoff (5 min → 6 h, four tries) before giving up loudly; SMTP keepalive on. A4 not A5: a give-up is a watchdog error, nobody is told unprompted. Watching: a makerspace_mail_retry warning followed by "Retry N delivered" on live is the acceptance; silence proves nothing. Never enable the smtp module's own "send by queue" — it has no runner. |
| 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 | 3 | 2 | watch | 2026-09-29: publishing a Notice now also posts it to #members (the notice form warns first). Watching: one post per notice, no duplicates. 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 | changing | 2026-09-28: a reported disconnect was a false alarm (the errors came from a Drush status-report run as anonymous; tokens valid, hourly auth check passing, no backlog). Reconnect and recovery runbook: docs/ops/XERO_RECONNECT.md in the site repo. Real outages already email staff once a day and self-recover on cron. 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 2026-10-01: the LLC path's first user, Aaron Monikowski, went two months unpaid. His business-routed bills sat approved but unpaid, and the site never told him Melio had to be set up for the business, because he had been paid personally before. Fixed by hand on live; it was the only stuck payee (131 PAYREQ bills paid Jun–Sep, median 2 days from bill to payment). Staged xero_bills_sync 7d153cd: onboarding judged per pay-to contact, a staff "move open hours to the business" step, and a daily alert for approved bills >7 days past due. |
| 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 for unclaimed payments (staged 2026-10-01: a daily alert for approved bills nobody has paid, which is the other half; a Melio-unclaimed payment still reads PAID in Xero and stays invisible) — 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 | changing | 2026-10-01: recognition formalized in code (STAGED). JR's policy: t-shirt on the first day volunteered, $15 toward lunch for any day of 3+ hours, hoodie after 5 days; counted from shifts and tabling, handed out from /tasks/thank-yous, and a shirt or hoodie given comes off the store's stock. "How I like to help" (/tasks/preferences) replaces the never-followed-up outreach webform, with a staff volunteer pool. 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 |
| Volunteer opportunity board | 4 | 3 | 3 | changing | 2026-10-01: phase 2 STAGED for that night's deploy (makehaven_tasks ed09810) after Kate's feedback (two ways to post, a messy tile, an overwhelming list): one Post form for every kind, time slots per shift, tabling as its own type, one "Volunteer opportunities" section with For you / New tags, a 2-day roster reminder (phase 1 never had one) and a thank-you email. Also found: the slack_connector webhook ignores the channel override, so #volunteers posts may land elsewhere until the job-board tab's SlackBot change ships. Watching: first real multi-slot post filled through the board; Kate's first-week verdict. Added 2026-09-29 — phase 1 shipped live that day. /tasks is the one place for "things people can do": tasks to claim plus opportunities that gather interest ("I'm interested" + note) before staff approve at /tasks/approvals. Recruitment posts to #volunteers; an opportunity short at its decide-by date is declined quietly (email to the interested only); enough interest emails staff@ to approve. Phases 2 (drafts from issue reports) and 3 (tabling type) are planned. Watching: whether opportunities reach approval through interest; kill criteria in the plan. |
| 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. Noted 2026-09-14: the location map on the DEI tab geocodes on request and empties whenever crawlers push it past Nominatim's rate limit (60 failures on 09-11 and 09-14) — a display fault, the data itself is unaffected |
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 | 2026-09-29 finding, fixed: the staff release summary missed six changes (flips made on live between deploys, a content change, sub-changes inside multi-commit sections) because it read only deploy-day headings and never compared them with the code. release-staff-summary.php now reconciles against every commit since the previous live deploy and refuses to pass with anything uncited; flips on live get their own APPLIED LIVE section. 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 |
| Public KPI dashboard | 4 | 3 | 3 | watch | Added 2026-09-14 by the weekly pulse — nobody had a row for the dashboard as a thing that serves pages, only for the packets built from it. The public /makerspace-dashboard (anonymous holds view makerspace dashboard) renders the finance, retention, education, DEI and operations tabs from ~60 chart builders. Broken right now: the Education tab has returned HTTP 500 to everyone since 2026-09-09 18:47, when the "chart correctness review" commit (1dc5c6a) shipped a query that joins a badge-access table on the req alias five lines before req is joined. A one-line reorder; found five days later by the pulse's watchdog read rather than by anyone looking at the page, which is why A is 4 and not 5. Also noted the same day: the DEI location map geocodes live on every request and crawlers push it into Nominatim's rate limit, so the map renders empty. Both logged in the site repo's AI_ISSUES 2026-09-14 (live, root 68c189cc44): Education tab fixed (join order), 200 signed out, KPIs warmed. Watching: no new 500s on /makerspace-dashboard/* for a week. |
| 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 | changing | 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 optimizable → changing 2026-09-30 (staged, not yet on live). Re-measured with answers logged: 27 member questions 08-21→09-30 from ~12 members; the navigator said "I'm not sure" to 8 of 10 (one member types bare tool names and it had no tool list), the tool assistant 7 of 17 plus one invented link; 80 of 864 members had switched every assistant off; the navigator never rendered on profile or badge-catalog pages (members get the other theme there); the staff review console had never been used. Staged fix: both assistants get the full tool and badge index, move to gpt-5.4-mini, strip links to pages that do not exist, the navigator appears on appointments and door-access pages, the hub search box hands unanswered searches to it, and the weekly pulse now reads mh:ai-failed-answers — which is the owner loop this row never had. Watch the "not sure" rate against 56% (navigator 80%) for two pulses after deploy |
| 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 61 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
81 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 218 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.