Stripe delivers webhook events in the endpoint's API version. Under a
version later than the 2025-02-24.acacia stripe-go v81 pins, the
payload-shaped invoice handlers projected every invoice with no
subscription link and no lines, and every payment with no payment
intent or method.
- Invoice events read only the invoice id and run ReconcileInvoice,
which reads the invoice, its lines and its payment intent's charges
back at the pinned version and converges the core rows under a
per-invoice lock, in any event order. A payment row is one attempt,
keyed by its Stripe charge (Stripe store migration 00005).
- Two clocks: created_at is when the console wrote a row; invoices gain
issued_at and payments paid_at (core migration 00022, backfilled),
and every invoice and payment list, the activity feed and the
composite's latest invoice read those.
- An hourly and at-boot invoice sweep reads back invoices never read,
open invoices once a day, and invoices of dead-lettered events; the
provider page states when it last finished. The first run is the
repair of invoices projected so far.
- The API version skew is logged once per version, and
payment_method.attached links payments recorded before it.
- A restricted key now needs Invoices read and Charges read.
- scripts/db-reader-role.sql maintains console_reader, a login role
that reads the console's tables without personal data, documented in
docs/database-management.md and checked against a golden column list
by TestConsoleReaderColumns. The demo snapshot keeps owners and
privileges so the role's checks hold on it.
- The demo seed and test fixtures carry issue and paid times.
status/design-feedback.md gains four upstream notes, one written when
#60 was closed.
OpenSpec change stripe-invoice-reconcile is archived and its deltas
synced into openspec/specs/. Follow-ups are #150, #151 and #152.
The contained runner that drove the 2026-09 security audit, the README
review and four rounds of design ideation lived only inside the ignored
notebook. It moves to scripts/agent-runner/: the prepare, tools, audit,
ideation and teardown scripts, the compose and container files, the
allowlist proxy and the prompt templates, with the paths that assumed
the notebook fixed and findings written to a caller-named directory. Run
outputs, transcripts and the round-specific sheet scripts stay behind.
docs/agent-runner.md states the method: the disposable git archive
copy and the fail-closed proxy, how a task is shaped, union rather than
intersection of findings across models, separate adjudication of every
finding against the source, and the evidence a finding must carry.
A notebook's lessons survive only if they are written into a tracked
home before the decision lands, so landing a change now has three
pieces. The contract: a "Landing a change" section in MAINTAINING.md and
a fifth item in the UI definition of done. The labor: a harvest skill
that inventories the notebook, names each file's kind and routes
findings to the owning docs page, reasoning to the change's design.md,
problems to issues.md and reusable tools to a tracked home. The gate:
scripts/notebook-citations.sh, a git grep over what git would commit for
the notebook's directory shape, which make lint runs after the program's
own lint, and scripts/archive-gate.sh, which refuses an mv into the
archive or an openspec archive while the check reports hits.
The gate is one script that any harness calls with the command it is
about to run; Claude Code reaches it through a PreToolUse hook and
opencode through a plugin. Both checks live in scripts/ rather than the
lint subcommand because they are repository hygiene, not part of the
shipped program. The generated openspec-archive-change skill is not
edited; the post-archive reminder carries the harvest step instead.
Introduce a commercial license option alongside AGPL-3.0-only, require a
CLA for contributors, and document the terms in COMMERCIAL.md and
NOTICE. Add a script to stamp SPDX headers on Go files and apply it
across the tree.