Skip to content

Promote QA → production (26 commits) - #1263

Merged
peterdrier merged 26 commits into
nobodies-collective:mainfrom
peterdrier:main
Nov 3, 2026
Merged

peterdrier merged 26 commits into
nobodies-collective:mainfrom
peterdrier:main

Conversation

@peterdrier

Copy link
Copy Markdown
Collaborator

Summary

Batched promotion of QA-tested changes from peterdrier/Humans:main to production.

All issue/PR refs are qualified per memory/process/issue-refs-qualified.md — peterdrier/Humans#NNN for peter-fork PRs, nobodies-collective/Humans#NNN for upstream issues.

Commits

DB changes

  • LegalDbContext (Consent): 20261102063239_PersistReConsentNotificationBatches — sequencing verified
  • HoldedDbContext (Holded): 20261030154917_HoldedLedgerEntryIdAndChecked — sequencing verified
  • UsersDbContext (Users): 20261101142832_AddUserMarkedFakeAt — sequencing verified
  • WorkgroupsDbContext (Workgroups): 20261103113444_WorkgroupMeetingDescription, 20261103134311_WorkgroupRecurringMeetingsAndNotes (apply order) — sequencing verified (end-of-chain, snapshot updated, newest Designer ≡ snapshot, has-pending-model-changes clean)

All four contexts: new migrations sort after upstream's chain tip, snapshot modified, has-pending-model-changes reports no changes.

Operator note: peterdrier#2237 adds the feedback.delete-screenshots data migration on /Debug/DataMigrations; per its commit it must be run on production before the Feedback-removal PR merges.

Test plan

  • Rebase merge (each PR is already squashed)
  • CI green on production
  • Verify EF migrations apply cleanly

Generated by Claude Code

peterdrier and others added 26 commits October 30, 2026 19:59
* docs: freshness sweep — 106 entries (upstream@f71de1eb)

Updated:
- dependency-graph, service-data-access-map, authorization-inventory
- about-page-packages, docs-readme-index
- 76 editorial docs (Board read-only access, section-owned roles,
  authenticated-by-default, folded section seams, event scoping)

Pruned: docs/superpowers/plans/2026-09-25-workgroup-budget.md

Flagged for review (see docs/freshness/last-report.md):
- 7 questions

Mode: diff
Previous anchor: cb3d8b3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA5iJNYcyZq3Yfh7pjv

* docs: drop derived counts from sweep docs and catalog prompt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA5iJNYcyZq3Yfh7pjv

---------

Co-authored-by: Claude <noreply@anthropic.com>
The roster is the kitchen assembling a few hundred people's allergies, not
someone looking up one person's health data, so a reveal records one scope
row (actor, week or day, person-row count). The per-member rows and the
member's reveal list on /Cantina/Dietary go. Rule atom updated.


Claude-Session: https://claude.ai/code/session_0186neA56jhtewg3fMbWo2Jd

Co-authored-by: Claude <noreply@anthropic.com>
* Holded single mirror, phase 1: mirror fields, reads and pages

- Ledger lines keep Holded's entry id and its manual Checked tick
  (nullable columns, generated migration).
- The service no longer loads the whole ledger table: balances, sums
  and counts are grouped in SQL, statements load only their entries.
- IHoldedService gains IsConfigured, GetEntryAsync, GetPurchaseLinesAsync,
  GetOpenAmountsAsync, GetSumsByAccountAsync and GetChartAsync.
- /Holded becomes a summary; new /Holded/Accounts (grouped chart with
  English PGC names) and /Holded/Calls; account and entry pages show the
  entry id and Checked. The Finance doc-sync row and the
  IHoldedFinanceService call behind it are gone.
- IHoldedClient's remaining external callers recorded as CENTRAL-105.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA66h1oBWsk6677PEqK

* Add Holded's own spellings to the PGC names; trim name tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA66h1oBWsk6677PEqK

* Drop Holded tests that repeat the service-level read tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA66h1oBWsk6677PEqK

* Holded: report an unsynced Checked as null; emit details open only in a conditional

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA66h1oBWsk6677PEqK

* Holded: English names for Holded's chart wording; open document amounts count refunds

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA66h1oBWsk6677PEqK

---------

Co-authored-by: Claude <noreply@anthropic.com>
* Holded single mirror, phase 1: the IHoldedWrite face

Ensure-style writes taking Humans keys and returning outcomes, never
Holded ids: creditor contact, purchase document, expense account,
purchase document delete (refused for foreign notes or payments, then
a ledger sync), and the journal entry post. Contacts now read
custom_id and type for the lookup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA66h1oBWsk6677PEqK

* Holded: whitespace format in HoldedWriteTests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA66h1oBWsk6677PEqK

* Holded write face: name match across creditor-range contacts, live expense-account check, notes as the document key

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA66h1oBWsk6677PEqK

---------

Co-authored-by: Claude <noreply@anthropic.com>
* Add section-map skill: C4 component map of one section

Draft from the trial runs on Teams and Cantina: screens on top, services
in the middle, repository at the bottom, neighbours in a slim column,
public interfaces on the section boundary, walkthroughs, drift last.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA795R7Sjqt4qB9GkgL

* section-map: caching decorator row, controller reads of other sections

From the Camps trial run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA795R7Sjqt4qB9GkgL

---------

Co-authored-by: Claude <noreply@anthropic.com>
… data (#2225)

* List every third-party processor on the Privacy page

The page named only Google OAuth and Google Workspace, but member personal
data also goes to Gmail relay, Google Maps, Stripe, Holded, Ticket Tailor,
MailerLite and Anthropic. A GDPR disclosure must name them all; the list now
sits in a delimited block so future integrations know to extend it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BqfQVmKktFiD5NkbMvXo2B

* privacy: drop unclear 'pending consents' from Anthropic entry; drop redundant end-of-list comment

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BqfQVmKktFiD5NkbMvXo2B

* privacy: registered office is Madrid, not Andalucía; add NIF

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BqfQVmKktFiD5NkbMvXo2B

---------

Co-authored-by: Claude <noreply@anthropic.com>
…talog (#2226)

* docs(readme): match config, features and milestones to the current app

The configuration tables named keys from before most integrations existed
and implied dotnet run reads .env; the contributing link pointed at a file
that lives under .github/. Milestones collapse to one row per month.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BqfQVmKktFiD5NkbMvXo2B

* docs(github): issue-template sections follow src/Sections

The Section dropdown still offered Legal, Board and Profile, and pointed at
docs/sections/, which no longer holds section docs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BqfQVmKktFiD5NkbMvXo2B

* docs(processes): list the skills that exist today

/sprint, /maintenance, /code-review, /codex, /gemini, /resharper and
/nuget-vuln-check are gone; plan-sprint, section-doctor, the debt and
freshness skills, fix, check-pr, reuse-review and others were missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BqfQVmKktFiD5NkbMvXo2B

* docs(contributing): fix the README link and name the rules that win

The setup link resolved to .github/README.md, and design-rules.md was
presented as the top rule source when AGENTS.md and Peter's rules sit
above it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BqfQVmKktFiD5NkbMvXo2B

* docs(guide): correct Getting Started and Glossary against the code

Signup now runs through the /OnboardingWidget name gate rather than /Guest,
the setup page explains tiers instead of offering a selector, and the help
button no longer offers feedback. Glossary camp-lead, season-status, sound
zone, section and Shifts Admin entries were out of date.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BqfQVmKktFiD5NkbMvXo2B

* docs(readme): point dev login at the login page panel

/dev/login has no index action; the personas are offered on /Account/Login.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BqfQVmKktFiD5NkbMvXo2B

---------

Co-authored-by: Claude <noreply@anthropic.com>
…ed one never reads as failed (#2227)

* doctor(Consent): open run file

* doctor(Consent): target shape covers the member-review lane

* doctor(Consent): sync job and its runner are section-private

Only Consent's Section.cs names SyncLegalDocumentsJob; its sibling job is already internal.
Rule: memory/process/reuse-first-change-discipline.md (public surface needs a reason).

* doctor(Consent): every refused signature gets a message, a recorded one never reads as failed

A NotFound refusal showed nothing; a failed onboarding report after a recorded consent showed
'submit error'. Review gates on the same name check Submit refuses on, from the loaded user.
Rule: memory/code/comments-earn-their-place.md (comments kept to the why).

* doctor(Consent): docs and comments say what is true now

IConsentWrite's summary said the caller audits; RecordConsentCheckAsync audits itself. Consent.md
named the suspension as Consent's job (it is Users'), narrated removed navs three times and carried
lane and dated labels. authorization.md now lists the policy Consent registers for Onboarding.
Rule: memory/code/comments-earn-their-place.md.

* doctor(Consent): run file, debt ledger

* doctor(Consent): PR number

* doctor(Consent): a failed cache reload after a recorded consent no longer reports the signature as failed

The decorator already evicted every alias before reloading; the reload error now logs instead of
propagating to the controller's error flash. Codex round 1 on #2227.

* Governance: statutes empty-state string in its own resx

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA4NJZtkCuc6yysafYp

* Consent: record the purpose Peter approved

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA4NJZtkCuc6yysafYp

---------

Co-authored-by: Claude <noreply@anthropic.com>
* Read Workgroups expense accounts from the Holded mirror (CENTRAL-105)

* Use the Holded read service for Expenses sync status (CENTRAL-105)

* Remove unused purchase document detail fields

* Remove unused Holded connector fields

* Move ticket sales display ordering into the controller

* Move ticket dashboard and holdings ordering to renderers

* Keep workgroup document ordering in the views

* Move workgroup member ordering into the views

* Audit camp image reordering with the acting manager

* Audit manual camp historical name changes

* Localize container breadcrumb navigation for screen readers

* Clean placement images after deleting a container

* Use shared invariant date formatters in Razor views

* Keep creditor contact matching in the pure duplicate matcher

* Reuse the shared timestamp pattern for migration snapshots

* Retire the drained invalidator warning exception

* Preserve Unicode when truncating HTTP error metadata

* Unify camp and team projections in the Store summary

* Resolve Store accounting reads through the existing reader

* Localize shared table filter controls

* Localize shared pager navigation labels

* Save grant program fields and manager changes atomically

* Save preapproval program fields and managers atomically

* Save rota fields and tag selections atomically

* Localize shared volunteer search result controls

* Save workgroup membership changes with their system log

* Save workgroup transitions and their required records atomically

* Save workgroup document transitions with their system log

* Keep campaign picker and coverage pie ordering in presentation

* Order shift department options in their presentation consumers

* Keep shift rota and dashboard chart ordering in page builders

* Order store summary products in the view

* Order board and team detail members in controllers

* Order cash flow category rows in the view

* Reject expense accounts without Holded identities

* Order disclosed assembly ballots in their views

* Order department coverage in the dashboard page builder

* Order coordinator activity in the dashboard page builder

* Remove redundant expense report repository ordering

* Order expense review reports in the controller

* Order shift signups and duration buckets in presentation

* Order daily email counts in the dashboard controller

* Keep program grant ordering in view models

* Order expense reconciliation lists at rendering boundaries

* Order Holded actual documents and managed accounts in views

* Order event category and venue lists in controllers

* Order email flag diagnostics in the Google controller

* Order onsite roster names in the controller

* Order assembly vote lists in their presentation consumers

* Order Workspace accounts in the admin controller

* Order visible email addresses in the profile card

* Order admin legal documents in the controller

* Keep preference ordering at output projections

* Include attendee-only matches in audience ticket years

* Order profile languages in presentation projections

* Keep profile history ordering at its consumers

* Keep contact-field ordering at presentation and search boundaries

* Order calendar occurrences in their rendering controller

* Order personal calendar commitments at output boundaries

* Keep participation ordering in the GDPR export

* Order machine notifications in the response controller

* Reject unidentified Holded contacts and sales documents

* Keep workgroup meeting ordering at its consumers

* Order workgroup log entries in the timeline view

* Order campaign code summaries in Tickets

* Keep team role ordering at its consumers

* Order unmatched Holded documents at their rendering consumers

* Keep expense-account display ordering in its consumers

* Move approved-event display ordering out of the cache

* Order camp galleries at their rendering consumers

* Order budget year lists in their views

* Reuse the Google email-owner picker for account audits

* Keep domain-group ordering in its consumers

* Order container galleries in their shared views

* Order creditor bindings at their rendering consumers

* Keep ranked-ballot display ordering above the service

* Keep draft-option ordering in the form mapping

* Reuse one authored option sequence when counting ranked votes

* Order duplicate-document previews in their view

* Order survey approval queues in the controller

* Order identified survey respondents in the results builder

* Leave Holded chart display ordering to its consumers

* Order Holded sync states and call months in the controller

* Order Holded account groups and rows in the controller

* Order survey result questions in their presentation consumers

* Order camp role displays in their controllers

* Order ticket sales buckets in report consumers

* Order application history in detail controllers

* Order survey choice results in presentation consumers

* Share current consent version selection across snapshot consumers

* Order migration reports in their presentation consumers

* Order agent refusal buckets in the status view

* Reuse budget year hydration for active-year reads

* Save ticketing projection parameters and forecast rows atomically

* Remove unused Teams repository persistence methods

* Order no-show history in the profile controller

* Share shift assignment notice failure handling

* Reuse the detached issue lookup for mutations

* Save camp member removals and role cascades atomically

* Finish started account merges despite request cancellation

* Save camp membership and role assignment together

* Commit application approval and membership tier grants atomically

* Save application and membership tier expiry corrections together

* Share department identity resolution across shift metrics

* Reuse complete Drive folder paths for resource lookups

* Commit bulk event imports atomically

* Finish ticket contact imports after request cancellation

* Share post-provisioning steps in contact imports

* Share survey question preparation across authoring saves

* Remove unused internal mutation forwarding paths

* Reject approved purchases without pending balances

* Verify new invoice totals before freezing store orders

* Reject malformed Stripe webhook payloads without throwing

* Reject unsupported currencies in Holded document reads

* Honor fractional Holded Retry-After dates

* Reject incomplete TicketTailor event summaries

* Require vendor validity before accepting issued tickets

* Reuse the MailerLite audience snapshot for sync

* Save receipt replacements and decline resets atomically

* Reassign the author of moved preapproval managers

* Commit grant merge revocations and reassignments together

* Commit camp membership and role merges together

* Commit ticket ownership and transfer merges together

* Reuse the enforced Google group settings mapping

* Reuse the group email for inactive resource lookup

* Share Google credential parsing across input sources

* Reject incomplete Cloud Translation responses

* Reject missing MailerLite subscriber status

* Validate MailerLite group identities before caching

* Count distinct humans in MailerLite drift reports

* Reuse the dietary profile mapping for updates

* Consolidate Gate vendor mirror job entry points

* Reassign signup actor references during account merge

* Include volunteer build status in GDPR exports

* Select active approval metadata for fallback membership tiers

* Reuse the membership tier mapping for updates

* Remove redundant service-account email cache state

* Share service-account JSON loading across Google clients

* Debt review: revert 3, repair 3, fix 1 ledger row

Reverted: 2743fb5 Localize shared volunteer search result controls — only rendered on coordinator/admin pages; localization-admin-exempt forbids new keys there
Reverted: ba09020 Reject unsupported currencies in Holded document reads — pre-empts open decision #2040 (same throw reverted before in dbb50c6: one foreign doc fails the whole sync)
Reverted: 0c66eba Remove unused purchase document detail fields — HoldedWrite (#2222, now merged from main) reads Id and PaymentsTotal; the branch no longer built
Repaired: 34135ac Order calendar occurrences — Calendar.md still said the service orders the merged list
Repaired: a674ac8 Workgroup transitions atomic — Workgroups.md named the renamed ClosePendingRequestsAsync
Repaired: ef2445f Reject expense accounts without Holded identities — Finance.md said "vendor IDs"
Fixed: SHIFTS-10 — account merge now folds VolunteerBuildStatus (source-only rows move, survivor wins a collision, same rule as GeneralAvailability); row closed
Fixed: dotnet format whitespace failures (code-quality job) in 19 files

Review-round: 1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA6HSvWAiDr4gVc7Lff

---------

Co-authored-by: Claude <noreply@anthropic.com>
The in-app notice for a new or re-consent-required legal document went
to every active member while the email went only to the document's team.
Both now use the team's active members.


Claude-Session: https://claude.ai/code/session_0186neA4NJZtkCuc6yysafYp

Co-authored-by: Claude <noreply@anthropic.com>
* Feedback: data migration that deletes every screenshot

Removing Feedback drops the only index to its screenshots, so they must
be deleted first. feedback.delete-screenshots lists every file under
uploads/feedback on /Debug/DataMigrations and deletes the whole prefix
through IFileStorage. Run it on production before the removal PR merges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA6c1saziAgfVAzPNbR

* Feedback screenshot cleanup goes through IFileStorage only

Status and Apply now live on FeedbackService: the report rows index the
keys, IFileStorage confirms which still exist and deletes them, then the
whole uploads/feedback prefix so orphans go too. No direct filesystem
access and no parallel root.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA6c1saziAgfVAzPNbR

---------

Co-authored-by: Claude <noreply@anthropic.com>
…2234)

* Move the Consent Coordinator review queue from Onboarding to Consent

The queue's verdicts (clear, flag) live in Consent's consent_reviews, so the
screen now lives there too, at /Consent/ReviewQueue (old /OnboardingReview
URL dropped, no redirect). Reject still goes through Onboarding's
IOnboardingIntake.RejectSignupAsync, which also changes account state,
teams and mail. Onboarding no longer clears or flags; it keeps the
rejection record and the Pending threshold write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA4AZ7wVcJxgC1vgdJZ

* Point the e2e review-queue specs and the Users slot doc at the moved queue

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA4AZ7wVcJxgC1vgdJZ

* Flag fake accounts in Users; seeders stop writing Consent verdicts

Users owns a "not a real person" marker (User.MarkedFakeAt, read as
UserInfo.IsFake), written only by IUserWrite.MarkFakeAsync (idempotent,
audited once). The gate-terminal seeder marks on every password set and
the dev persona seeder on every role-persona sign-in, instead of
recording a Cleared consent check through IConsentWrite. Consent's
review queue skips fake accounts. Guests stay unmarked.

Migration: one nullable column on users (AddUserMarkedFakeAt).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA5iRoj68ARVJsdxU78

* Skip fake accounts in the shared review predicates so the pill and meters match the queue

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA5iRoj68ARVJsdxU78

---------

Co-authored-by: Claude <noreply@anthropic.com>
* CI: label and comment on PRs that conflict with main

Re-checks every open PR on each push to main (and a PR's own pushes), adds a
merge-conflict label plus one comment so the session watching the PR wakes
and merges main in, drops ready-to-merge, and removes the label once clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA427NKUHL8tCv9hvog

* CI: per-PR concurrency groups so queued conflict checks aren't dropped

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA427NKUHL8tCv9hvog

* CI: conflict check survives partial failures and concurrent runs

Drop ready-to-merge on every conflicted run, comment before labelling so a
failed comment is retried, and treat a 404 label removal as done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA427NKUHL8tCv9hvog

---------

Co-authored-by: Claude <noreply@anthropic.com>
---
updated-dependencies:
- dependency-name: Markdig
  dependency-version: 1.5.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps AngleSharp from 1.8.6 to 1.9.0
Bumps HtmlSanitizer from 9.2.1039 to 10.0.1120

---
updated-dependencies:
- dependency-name: AngleSharp
  dependency-version: 1.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: HtmlSanitizer
  dependency-version: 10.0.1120
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
updated-dependencies:
- dependency-name: Google.Apis.Auth
  dependency-version: 1.78.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Route Finance journal postings through Holded write outcomes

Advance CENTRAL-105 using the existing write contract, retaining retry tags and journal amount markers. Audit confirmation outcomes instead of connector references. Close CONSENT-1 under the standing policy against controller-policy pinning tests.

* Consolidate notification delivery in the emitter

Share preference filtering, copy limits and persistence between individual sends and role broadcasts while preserving their resolution scopes. Remove the redundant single-notification repository write.

* Preserve consent reminder cooldowns across account merges

Read the latest reminder stamp across each survivor's identity chain instead of ignoring stamps left on merged-away accounts. Keep historical rows and existing export and erasure behavior intact.

* Remove obsolete single-consumer consent helpers

Keep required-consent projection in its sole service owner now that the cache delegates to it. Inline the single-use chat-terms name in its document definition and retain the existing shaping assertions through the service boundary.

* Report term renewal reminder failures after completing fanout

Collect email and sent-stamp failures while attempting the remaining recipients, then fail the job so failed reminders can retry. Preserve best-effort in-app notices and successful reminder stamps.

* Report incomplete account deletion runs as failures

Attempt every due account, then propagate collected anonymization failures for job visibility and retry. Keep post-erasure confirmation delivery failures terminal and preserve their identity-safe logging.

* Record suspension sweep outcomes at the job boundary

Count every completed sweep once, including empty candidate sets and unchanged users. Remove run-success accounting from the domain service while retaining its per-member suspension metrics.

* Reuse canonical user snapshots for notification addresses

* Reuse canonical user emails throughout ticket transfers

* Fail scheduled MailerLite syncs when work remains incomplete

* Audit system-team membership changes before Google provisioning

* Audit Holded expense-account creation before saving its registry row

* Keep failed pre-approval reminders eligible for retry

* Exclude internal contracts from the public interface surface report

* Select relevant search hits before applying result limits

* Select the strongest people-search matches before the safety cap

* Report incomplete survey reminder sweeps and reuse delivery identities

* Reuse renewal reminder delivery identities after failed stamps

* Reuse assembly email deliveries within each announced deadline

* Reuse re-consent reminder deliveries within each cooldown cycle

* Audit each membership expiry within its tier mutation

* Include Base interfaces in public surface reports

* Consolidate camp membership decision notices

* Finish lifecycle inbox updates after committed state changes

* Commit deletion requests and access revocations atomically

* Record failed email outbox job runs

* Suspend every verified Workspace mailbox before erasure

* Keep MailerLite erasure scoped to the raw account

* Remove unused email projection query methods

* Separate shift signup notices from state changes

* Keep rideshare request answers within their event

* Distinguish riders from drivers when matching ride requests

* Resolve active survey audiences from account state

* Derive profile setup mode from account state

* Reuse survey answer context across response exports

* Isolate survey translation preparation from authoring

* Preserve gate admit timestamps in vendor mirror jobs

* Format pending deletion dates in the UI culture

* Unify feedback triage persistence and audit ordering

* Move user summary display ordering to presentation

* Localize coordinator membership notices by recipient language

* Localize coordinator shift notices by recipient language

* Resolve issue notice recipients and actors before self-exclusion

* Keep saved issue notices progressing after role lookup failures

* Move pending consent display ordering to rendering consumers

* Continue preapproval manager fanout after individual delivery failures

* Exclude historical ticket orders from shift dashboard metrics

* Report assembly sweep delivery failures after completing remaining work

* Persist re-consent notification batches across unchanged sync retries

* Report legal sync failures after completing successful document work

* Separate Rideshare interest notices from mutation rules

* Match notification search text literally

* Match shift tag names literally when searching and creating

* Compare pending merge email addresses literally

* Match legacy Google group addresses literally

* Reject group links whose resolved identity is already active

* Reuse the active resource type query across Google readers

* Reuse resolved user snapshots for Governance email recipients

* Reuse resolved user snapshots across survey email workflows

* Reuse resolved user snapshots for campaign email recipients

* Reuse recipient snapshots in preapproval email delivery

* Reuse grant recipient snapshots while preserving email recovery

* Reuse reporter snapshots for support reply emails

* Reuse member snapshots for SEPA payout notices

* Reuse submitter snapshots for expense approval notices

* Reuse recipient snapshots for workgroup lifecycle emails

* Reuse caller snapshots for composer self-send

* Preserve committed deletion requests when confirmation fails

* Attempt every committed consent suspension audit

* Align MailerLite audience previews with canonical email selection

* Isolate Teams Google Group membership calculation

* Reject grouping separators in SEPA payout inputs

* Reject grouping separators in workgroup budget amounts

* Keep Stripe payment recovery with Store payment ingestion

* Localize role assignment notices for their recipients

* Attempt every committed system-team membership audit

* Audit committed coordinator-role reconciliation changes

* Attempt every membership audit after team deactivation

* Commit camp deletion and its audit together

* Commit image and container deletions with their audits

* Keep best-effort audit writes outside business transactions

* Audit completed workflows after business transactions commit

* Audit replacement provisioning only after its outer commit

* Defer preapproval effects until expense transactions commit

* Debt review: revert 0, repair 2, fix 0 findings

Repaired: 5e0ad14 Audit each membership expiry within its tier mutation — Governance data-access.md now says the TierDowngraded audit follows the commit (3e17a43 moved it).
Repaired: b9b320a Commit deletion requests and access revocations atomically — IAccountDeletionService XML doc no longer claims the audit commits with the marker.
Fixed: dotnet format whitespace violations failing the code-quality check (10 files).

Review-round: 1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA51VTAAwfAjB4ssCdZ

---------

Co-authored-by: Claude <noreply@anthropic.com>
* chore(deps): Bump CsvHelper from 33.1.0 to 34.0.0

---
updated-dependencies:
- dependency-name: CsvHelper
  dependency-version: 34.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Port CSV code to CsvHelper 34

CsvHelper 34 is a rewrite: CsvConfiguration/ClassMap are gone, options are
init-only records, and zero-field records are refused. HumansCsv now builds
CsvReaderOptions/CsvWriterOptions with the same conventions, gains
WriteBlankLine for section breaks, and the bulk-event record uses [CsvRecord].

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA5kJubQyXLP8RwjZSS

* About page: CsvHelper 34.0.0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA5kJubQyXLP8RwjZSS

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
---
updated-dependencies:
- dependency-name: Ical.Net
  dependency-version: 5.2.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Move the GDPR contributor seam into Base

IUserDataContributor and UserDataSlice move from Humans.Gdpr/Contracts to
Humans.Base/Gdpr, namespace unchanged. Every data-owning section implemented the
seam, so 42 projects referenced the Gdpr section for two types; those references
are dropped. Gdpr is now referenced only by Users (IGdprService), Web, and the
Gdpr/Users/Web/Integration test projects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA78CpJeYKoxRTGsySg

* Trim Base csproj note on the Gdpr seam to its contract

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA78CpJeYKoxRTGsySg

---------

Co-authored-by: Claude <noreply@anthropic.com>
* Share one calendar event form across Calendar and Workgroups

Calendar publishes ICalendarEventForm and the _CalendarEventForm partial
(the workgroup layout: date + time inputs, time zone chooser, location,
description, all-day and recurrence where the host supports them).
CalendarEventForm.Resolve is the one conversion from posted local
fields to stored times.

Workgroup meetings drop their own UTC-only form for it: the zone
defaults to the organisation zone (Event:TimeZoneId, now readable via
ISettingsService.GetTimeZoneIdAsync), and meetings gain a Description
field separate from Minutes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA5AnnRepibQGTwJeEP

* Render the shared form's time inputs through ToInvariantTime

Inline asp-format date literals break the single-home rule (HUM0030).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA5AnnRepibQGTwJeEP

* Use NodaTime LocalDate/LocalTime on the shared calendar form

The Shell's LocalDateTime binder now also binds LocalTime from the
browser's seconds-free type="time" value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA5AnnRepibQGTwJeEP

* Point the recurrence picker browser test at the shared form

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA5AnnRepibQGTwJeEP

---------

Co-authored-by: Claude <noreply@anthropic.com>
* Move the audit append path into Base

IAuditLogService (with AuditLogEntrySnapshot) and AuditActionDefinition with its
contributor contract move from Humans.AuditLog/Contracts to Humans.Base/AuditLog,
namespace unchanged. Projects that only wrote audit rows or declared actions drop
their AuditLog reference. AuditLog is still referenced by Web, Agent (viewer),
GoogleIntegration (legacy reader) and the six sections whose views render
<vc:audit-log>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA78CpJeYKoxRTGsySg

* Drop orphaned AuditLog reference comments in Settings

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA78CpJeYKoxRTGsySg

* Point AuditLog data-access map at the Base location

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA78CpJeYKoxRTGsySg

---------

Co-authored-by: Claude <noreply@anthropic.com>
* Cancel a SEPA file the bank rejected, and collapse file cards to a summary

Cancel file withdraws every live transfer of a file in one save, all or
nothing, audits each, and emails nobody. File cards show count and total
and start collapsed once booked or cancelled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA6X9gPUU8STKsrznnB

* Serialize SEPA withdrawal and file cancel with booking

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA6X9gPUU8STKsrznnB

---------

Co-authored-by: Claude <noreply@anthropic.com>
* Repeat workgroup meetings and post meeting notes as log entries

Meetings take a recurrence rule and zone from the shared calendar form, and
Calendar's expander repeats them everywhere: the group timeline, the next
meeting card, the rhythm check, the community calendar and the personal feed
(one repeating VEVENT with a zoned DTSTART).

Notes after a meeting are a new Meeting notes log kind, optionally linked to
the meeting, posted from an Add notes button beside each started occurrence.
The meeting form's Minutes box goes; existing minutes still show.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA5AnnRepibQGTwJeEP

* Fix whitespace formatting in feed test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA5AnnRepibQGTwJeEP

* Refuse sub-daily meeting rules, count only real meetings as activity, keep notes editable

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA5AnnRepibQGTwJeEP

* Find a meeting's next occurrence however far off

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186neA5AnnRepibQGTwJeEP

---------

Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the db label Nov 3, 2026
@peterdrier
peterdrier merged commit 0841263 into nobodies-collective:main Nov 3, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant