world gains MapProduct and MapProductIn, which give a product a synced
Stripe mapping through the console's own upsert (Stripe id prod_<product id>,
no environment or the one asked for), and WithoutSet, which clears a
product's entitlement set through EditProduct the way the operator's edit
form saves a product with no set chosen. TestCatalogBuilders reads both back
through the console's queries. No builder writes a dead-lettered outbox
entry; a test that needs one makes it through the console's
MarkOutboxDeadLetter.
No production file changes and no existing builder writes anything new. Names
and results over ./internal/testkit/..., internal/entitlements,
internal/fulfillment, internal/server, internal/integrations/discourse/web,
internal/integrations/fedwiki/web and internal/integrations/stripe/workflows,
taken with the test database reset before each side, are equal at the parent
and here except the new test and its two subtests, which pass. The package
test/e2e/plan-management, which also builds through world, compiles under go
vet; it calls none of the new builders and no existing builder changed. 5
breaks, each caught by the new test; none dropped. Unprobed: MapProductIn(p,
false), which is the same statement with the flag false; the test reads the
live case.
stripetest.KeyMode returns the mode of the key the Stripe SDK holds when
it is called: "test" once Install has set its key, "" when the SDK holds
no key, "live" under a live key. A test passes it where the console
passes its configured key mode, and it reads the key at the same moment
production does, so a test that never installs the mock keeps the mode
it has today. The docs say so beside the other stripetest helpers.
No production file changes. The package's tests run with the app down on
freshly reset test databases at the parent and at this commit; the names
and results are equal apart from the one new test, which passes. One
break (reading a fixed key instead of the SDK's key) is caught by the new
test; the test has four cases, none unprobed.
A test can now put a line of its own among the statements a Counted
handle recorded, and have the Stripe mock hand it every call it
receives, so one list shows each Stripe call between the statements it
ran between, begin and commit included.
Statements.Note places a line after the statements recorded so far.
Names returns statements and notes in order; Total and ByName leave
notes out; Reset forgets them. MockBackend.OnCall is called for every
request as soon as it is recorded, before Err, PathErrs or Handle
answers it, so a refused request is seen too. The recording moved from
Call into a small record method, which keeps (*MockBackend).Call at its
cognitive complexity of 65. stripetest.Line prints a call as
"stripe <METHOD> <path>" and its form's pairs sorted by key, unescaped.
docs/testing.md names OnCall, Line and Note.
No production code changes, so no output changes: only test helper
packages and docs/testing.md are edited, and the build, vet, gofmt, lint
and deadcode checks pass with the lint snapshot unchanged. The names and
results of ./internal/testkit/..., ./internal/stripetest,
./internal/fulfillment, ./internal/integrations/stripe/... and
./internal/server equal the parent's (1767 at the parent, 1771 here;
every result pass), the four additions being the new tests
TestLineNamesTheCallAndItsSortedForm, TestOnCallSeesEveryCallInOrder,
TestOnCallSeesARequestErrRefuses and
TestNotesTakeTheirPlaceAmongTheStatements, after a database reset before
each side. Eleven breaks, each caught by the new test.
Not probed: OnCall called from concurrent goroutines. The mock appends
under its mutex and calls OnCall outside it, and no test of this series
calls Stripe from two goroutines at once.
dbtest.Q.Rows runs a query and returns a line of column names, then one line
per row in the query's order, values separated by " | ". NULL reads NULL, a
time reads in UTC as RFC 3339 with its fraction, bytes read as text, anything
else as fmt prints it. The query names its own order. docs/testing.md lists
Rows with the Q methods.
No production file changes. Shown to change no output: the tests of
./internal/testkit/... listed by name and result before and after differ by
the one new test, TestRowsPrintsColumnsThenRowsInQueryOrder, and every test
passes; the kit's imports guard still passes, and the lint snapshot has no
added or removed line.
5 breaks, each caught by the new test (a new test is proven on the new side
only); none dropped.
Unprobed: how the driver hands over each column type (uuid, numeric and json
arrive as text or bytes, both printed as text; which one a type takes is the
driver's choice, so the test does not name it), and the error branches (each
fails the test with the whole query folded onto one line, as Strings does).
The server's golden helpers (the comparer, the -update flag and the
normaliser that turns a fixture's ids into names) move into
internal/testkit/golden, so any package's tests can compare output with a
file under testdata. Ordinals is new: it numbers the UUIDs and UTC times
an act makes by first appearance, for the ids a fixture cannot name.
Normalizer.Add replaces the one place a server test appended to the
normaliser's unexported pairs.
Eleven server test files use the package; the -update flag keeps its
name. docs/testing.md gains the kit row and a bullet, and the kit's import
guard and the dead-code list know the package.
No output changes. No production file changes, and no file under
internal/server/testdata is touched; every server golden test passes
without -update and git status is clean under testdata afterwards. The
names and results of ./internal/testkit/... and ./internal/server are
equal at the parent and at this commit (1452 and 1458 tests and
subtests), apart from the five new tests and the import guard's new
subtest for the package. No assertion changes, so no assertion map.
13 breaks: 12 caught by the new tests, and the one that renames the
normaliser's <today> pair is caught by the golden test at the parent and
at this commit.
Unprobed: that a name outranks a pair in the server's goldens (their ids
are UUIDs and their pairs a date and the suffix, which never start at one
position, so the order is invisible there; the kit test probes it); the
server copy of the comparer at the parent (a break that makes it pass
everything cannot fail the goldens it compares); a migration test that
defines its own update flag (a documented rule, not code).
Statements.Names returns a copy of the recorded names in run order. A
transaction's begin, commit and rollback are among them, because pgx sends
them as statements the tracer sees, so a test can read where each statement
fell against the transactions around it. docs/testing.md lists it beside
ByName, Total and Reset.
No production file changes. The test packages under internal/testkit run
with the same test and subtest names and results at the parent and at this
commit, apart from the new TestCountedNamesStatementsInOrder (37 results
before, 38 after, each run after a reset of the test databases).
4 breaks, each caught by the new test: sorted order, a dropped first name,
the shared slice returned instead of a copy, and Reset leaving the names.
Unprobed: the mutex, because every caller records on one goroutine. That
rollback is recorded is pgx's behaviour; the test asserts it, but no
one-line edit of the kit removes it, so no break covers it.
No production file changes. authtest gains Cookie, which commits a session
and returns its cookie, for a handler wrapped in the session middleware,
such as the whole router, where Get and Post cannot put the session on the
request's context. docs/testing.md names it and the kit table's authtest
row says "signed-in sessions, their cookies, and in-process requests".
internal/server/new_handler_test.go (no database) builds the router from a
configured profile and a bare one (no Stripe key), with a fake route mount
and two UI mounts, and adds five tests. TestNewHandlerRouteTable writes one
block per registered pattern (the 97 literal HandleFunc patterns the lint
scan finds in internal/server and internal/auth, plus the two static-file
Handle registrations and the fake mount's three routes) to
testdata/routes.golden: the pattern the mux resolves a probe path to, and
the answer to an anonymous request, a cross-site request for a method that
changes state, and a signed-in member where the handler answers before any
read; then eight extra probes and the bare profile's checkout row. The
golden was written once with -update and read against the code: every
operator row answers 302 /login anonymously and 403 Forbidden to the
member, the cross-site writes answer the CSRF refusal except the fake
webhook (204), the two wrong methods and the bare checkout resolve to / and
the styled 404, and the body one byte over 1 MiB answers 413.
TestNewHandlerTrustsTheBaseURLOrigin, TestNewHandlerRefusedSignInIsStyled,
TestNewHandlerRefusesAMountTemplateOutsideItsKey and
TestNewHandlerPassesDepsToMounts cover the base URL's origin, the styled
refused sign-in, the key-prefix refusal and the Deps a mount is handed.
Shown to change no output: no production file is touched, and the names and
results of internal/server and internal/testkit equal the parent's apart
from the five new tests, which pass three runs in a row. The 47 breaks of
the plan, each applied to the new side after a green control run, are all
caught.
Left unprobed:
- The answers the golden records that code outside the router produces
(each handler's own answer, the auth handlers, the operator role guard,
each middleware's behaviour): each has its own tests, and no edit to the
router changes them; the golden holds them so a change shows.
- middleware.Timeout(32s): a short value races fast handlers, and a probe of
the real value waits 32 seconds.
- middleware.Logging and the order of RequestID before it: they write a log
line no test reads.
- The http.Server fields: Start keeps them, and nothing builds the server in
a test.
- The dashboard's root-only check: a later commit probes it.
- The real integrations' routes (FedWiki, the Stripe webhook, Discourse):
they mount from cmd/start.go, and the fake mount stands in.
- The Config cmd/start.go builds (including StripeKeyMode and BaseURL) and
the Start call with its exit path: no test builds the command's Config;
these tests build their own.
Four new tests pin what four catalog loaders show when a read that names or
counts per row succeeds, fails or has no querier: the actor each change of a
set's history names (a person's change names the person, the system's names
nobody, a failed person read and a missing identity querier name nobody), the
active rules each set in the sets list counts (a failed read counts 0), the
first unconfigured provider the setup checklist's integrations step names in
set order (a failed rule read skips every set), and the Stripe mapping each
price on a product shows (a failed read or no Stripe leaves every price not
mapped). Four rows join the read-count table, each measured at one statement
per row, as predicted: entitlement-set-history, entitlement-sets, setup and
product-prices. The table's page type gains the integration configs the setup
row needs.
world gains RuleChange, a person's rule-added commit through the batch commit,
which settles the carrying pools; Rule stays the system's unsettled path.
registerGoldenKeys takes a testing.TB so a read-count build can call it.
No production file changes, so no output changes: the goldens are untouched,
and over ./internal/server and ./internal/testkit/... the set of test and
subtest names and each result equals the parent's apart from the 18 new
names (4 tests, 10 subtests, 4 read-count subtests), none missing, none
changed, no failures; three runs in a row on a reset database are green.
19 breaks, each caught by the new test; the old side does not exist for a
test that is new. Unprobed, since no section of this step moves their code: the history's other
columns and paging, the sets list's failed-obligation count, the setup page's
other steps, the price rows' amounts and intervals.
Adds tests, one test-kit builder and goldens only; no production file
changes.
TestPlanLaddersGolden builds three ladders (Hosting with Basic and Shared,
Support with Shared, Empty with no tier), five products (a shared one, an
add-on, one with no category, a draft), three organization types (defaulting
to Hosting, to nothing and to the empty ladder) and an organization holding
Basic, and compares with files under testdata/operator_plan_ladders: the
topology's columns, rank rows, shared products, off-ladder strip and type
summary together with the ladder list (page); the ladder page's tiers with
their organization counts (detail); and the reorder preview's order for the
ladder's own order (preview). The page lists every ladder in the database, so
the files hold the fixture's ladders, products and types. The properties
subtest holds the whole grid to the ladders' own reads: rows ascend by rank,
every row has one cell per column, and every column, the fixture's or not,
counts what CountActiveAttachmentsByLadder returns and holds the tiers
ListTiersByLadderWithProducts returns. Each file was read against the fixture
before it was committed; a type whose default ladder has no rank-0 tier reads
as no default.
TestPlanLaddersReadFailures pins, per read (ladder listing, shared products,
ladder counts, tier reads, product list, rank-0 listing, organization types,
tier counts on the ladder page and in the preview), what the pages show when
that read fails.
world gains AddTier. The read-count table gains plan-ladders (4 reads per
ladder), plan-ladder-detail (1 per tier) and tier-reorder-preview (2 per
tier), each as the plan predicted; the table's request can now be a POST.
Names and results of ./internal/server and ./internal/testkit/... match the
parent apart from the new tests, which pass: TestPlanLaddersGolden and its
four subtests, TestPlanLaddersReadFailures and its nine, and the three
read-count rows. A second run without -update leaves the tree unchanged, and
the tests pass three runs in a row after a reset. Thirty-two edits to the
topology and the ladder code, each breaking one fact, fail the test that names
it.
Unprobed: each column's and list row's IsActive and description, the org-type
summary's display names (moved unchanged); the list's name filter and paging,
which no section of this step edits; the order of same-named products in the
shared index, which is unspecified today and which no section of this step
fixes; the reorder preview's classification path (rank 0 does not change in the
fixture); the topology's template rendering, which its own tests keep.
Adds tests, two test-kit builders and goldens only; no production file
changes.
TestProductsListGolden builds thirteen products (a ladder tier, an off-ladder
product, an add-on, one with no set, one with no rules, one priced in the other
environment, an unlisted, an inactive, a draft, a retired, one needing an
unconfigured provider, and two sharing a set) and compares the rows the loader
builds, and the page rendered from them, with files under
testdata/operator_products, with Stripe configured and not. Each file was
read against the fixture before it was committed, and each failure file against
the fallback that read gives every row when it fails. The golden files show the
verdict, note, set name, category and visibility of every row, the provider leg
naming the unconfigured provider, and both set cells naming the shared set.
TestProductsListReadFailures pins, per read of the page (shapes, set names,
prices, price mappings, set rules, resource keys, and the page itself), the
rows the loader builds when that read fails, and that a failed page read says
"Failed to load products" and builds no row.
world gains EditProduct and Lifecycle. The read-count table already holds the
products row at 0 per row.
Names and results of ./internal/server and ./internal/testkit/... match the
parent apart from the new tests, which pass: TestProductsListGolden and its
two subtests, TestProductsListReadFailures and its seven. A second run without
-update leaves the tree unchanged, and the tests pass three runs in a row
after a reset. Nineteen edits to the list code, its readiness helper and its
template, each breaking one fact, fail the test that names it. One of them
(the price reader told "configured" when Stripe is not) changes no output in
that state, because the mapping read is skipped there and the map stays empty;
it is run in the configured state with "not configured" instead.
Unprobed: the view model's ProductID, Name, Description, IsActive,
LifecycleStatus, Lifecycle and EntitlementSetID fields as individual facts
(the golden compares them, and the other breaks show it compares every
field); the skipped mapping read when Stripe is not configured, since no
output depends on it; the order of the six reads; the row order, which the
list query fixes; that two products sharing a set pass its id to the set and
rule reads once (the ids a batch read receives change no output, and the page
reads the sets in one statement either way).
Adds tests, three test-kit builders and goldens only; no production file
changes.
TestBillingListsGolden compares each list's data, in the default and the
all state, with a file under testdata/operator_billing for a fixture of two
organizations and four billing accounts (live, unrecorded, unmapped, test),
each with a subscription, an invoice and a payment. Each file was read
against the fixture before it was committed: the test-mapped row is hidden
by default with the line counting one, shown with the test badge in the all
state, the unmapped row reads not_mapped, and every row names its own
organization.
TestBillingListsKeepTheirFallbacksWhenAReadFails pins, per list, that a
failed organization read leaves the name empty and a failed mapping read
shows not mapped.
TestLookupInvoiceCandidatesNameTheirOrganizations pins the invoice
candidates' organization names and the organization id as their fallback.
The read-count table gains billing accounts, billing subscriptions, billing
invoices, billing payments and lookup rows. Each grow adds its rows on a new
organization named from a counter, and the lookup row is sent as an htmx
request. Measured per row: 2 for each billing list, 1 for the lookup, as
predicted.
world gains MapAccount, MapSubscription and MapPayment, which record no
environment, beside MapInvoice.
Names and results of ./internal/server and ./internal/testkit/... match the
parent apart from the new tests, which pass: TestBillingListsGolden and its
twelve subtests, TestBillingListsKeepTheirFallbacksWhenAReadFails and its
twelve, TestLookupInvoiceCandidatesNameTheirOrganizations and its two, and
the read-count subtests billing_accounts, billing_subscriptions,
billing_invoices, billing_payments and lookup. A second run without -update
leaves the tree unchanged, and the tests pass three runs in a row after a
reset. Thirty-two edits to the list code and the lookup, each breaking one
fact, fail the test that names it.
Unprobed: the goldens pin the loaders' other fields (dates, amounts, statuses,
account names, payment method, invoice numbers, Overdue, the pager and the
absence line) with no break, because no later section edits the code that fills
them; the breaks probe the fields the mapping and naming changes move.
TestBillingViewsFilterToTheKeyEnvironment now builds its rows through the
world kit (organization type, owner, organization, billing account,
invoices, payments, bare subscriptions and their Stripe mappings) and
drives the operator routes with a signed-in operator through authtest.
Each subtest is a method of billingViewsFixture and reads the parsed page
(the absence line, its switch, the invoice rows, the facet pills) instead
of searching the body for strings. The gocognit exception for the test is
deleted; its largest function now scores 10.
The kit gains BareSubscription (a subscription row and nothing else) and
the mapping builders MapInvoice, MapInvoiceIn, MapPaymentIn,
MapSubscriptionIn and MapAccountIn. seedPayment, seedSubscription and
seedAccountMapping are deleted with their last caller, with the unused
member session; seedInvoice stops returning the id.
No production code changes. Shown to change no output: names and results
of ./internal/server and ./internal/testkit/... are identical at the
parent and at this commit (1204 tests each, none failing). 17 breaks,
each caught by the new test; 15 of them also caught by the old test. The
payments and subscriptions noun breaks are new side only, because the old
check could never fail for them.
Assertion map rows: every row same or wider, none narrower. One general
weakening: the badge's exact markup (`>Test</span>`) is now read as
whitespace-collapsed text. Fixture differences: names and invoice numbers carry the world
suffix instead of the be<UnixNano> marker; the organization type comes
from OrgType; the organization has a default pool; the owner is a world person;
the signed-in operator is a person of its own. The parent and commit database
dumps agree apart from these differences.
Unprobed: the absence line's count, which TestAbsenceLineCountsUnderTheSearch
pins; the LIVE and NULL rows' lack of a badge in the all state, not asserted
before and not added.
TestGetOperatorInvoiceDetailPage builds its organization, billing account,
price, invoice and line through the test kit's world on the shared database
instead of raw INSERTs in a rolled-back transaction, signs in an operator
through authtest, and reads the page through htmltest. The kit gains
LineItem, which writes a line the way the invoice reconcile does. The
gocognit exception for the old function leaves .golangci.yml; the largest
new function is well under the limit.
No production file changes, so no output changes. The test and subtest
names are the same; the full result sets of ./internal/server and
./internal/testkit/... (1204 tests) are identical at the parent and here.
Eighteen breaks of the production code the page runs through were each
caught by the new test, and the seventeen that the old test can see were
each caught by the old test too; the Overdue badge's danger tone is proven
on the new test alone, because the old check (text-bg-danger anywhere in the
body) could never fail.
Assertion map: every old check is the same or wider, with no narrower row.
One general weakening, for markup that states no fact: the title's and the
id's utility classes are no longer asserted. The amount check reads the
"Amount due" cell alone; the old check passed on that cell or the line's
amount, so the line's own amount stays unasserted, as before. The old
retired-markup absences stay raw over the whole body.
Fixtures differ in ways no assertion reads: names carry the World's suffix;
the organization has a default pool; the owner is a world person and a
second person, the operator, is signed in; the product is published with an
active set; the price is the product's default, as CreatePrice makes a
product's first price; the rows commit and the truncate is gone. The parent
and commit database dumps agree apart from these differences (the parent's
taken before the 404 subtest, because reading the non-UUID id aborts the old
transaction).
Unprobed: the line's own amount, which neither test asserts alone.
Add world builders that write through the queries and functions the
console calls: BillingAccount, Invoice and Payment, IssueOpts.Note,
Extend (the Manage act's extension), ReorderTiers, RemoveTier and
EndSubscription (the reconcile's terminal branch). Subscribe now
remembers the pool it conferred on. No existing builder writes anything
new: IssueOpts.Note is empty by default and is then written as NULL, as
before.
Shown to change no output: only test-kit code and docs change; no
production file is touched. The tests of internal/testkit/..., internal/server,
internal/fulfillment and both integration web packages pass on a reset
database with the same names and results, and the lint snapshot is
identical to the parent's.
The two new tests (TestBillingBuilders, TestActBuilders) were checked with
15 breaks, each caught; none dropped. Each TestActBuilders subtest builds
its own World, and three more breaks (the floor, a subtest's failure
charged to the subtest, both payments read back) were each caught.
Unprobed: RemoveTier's refusal of a held tier (a Fatalf inside the
builder), EndSubscription's closing of scheduled changes (no fixture
schedules one on a subscription that ends), and an invoice's due date,
period and paid time (written as given).
dbtest.Counted opens the test database on a handle that records the name of
every statement run on it (a generated query by its query name, anything else
by its first line), with Reset, Total, ByName and Diff. A new table test,
TestOperatorPagesReadAFixedNumberOfTimes, grows a page by one row, then by two
more, and asserts that the second request runs 2*perRow more statements than
the first. Its first row is the products page at 0 per row, which holds today.
No production file changes, so no output changes. Tests of internal/testkit/...
and internal/server pass on a reset database, and the read-count test passes
three runs in a row. New tests: TestCountedRecordsEachStatementByName and
TestDiffListsTheNamesWhoseCountsDiffer (dbtest), and
TestOperatorPagesReadAFixedNumberOfTimes with its products subtest (server);
every other name and result equals the parent's. 8 breaks, each caught by the
read-count test (new side only).
Unprobed: the mutex in Statements (every handler under test runs its
statements on the request's goroutine, so nothing records concurrently);
pings and prepares going unrecorded (a ping happens only after a second of
idleness, and nothing in the module prepares a statement); Counted failing on
a database behind the migrations (ledgerBehind, which the Open test pins).
The storefront and consumers rows name the files that now hold the plan,
add-on and entitlement code; the integration guide drops member_products.go
from the files that stay in core and says the files of the split import no
Stripe package; the resource-pools
dimension states what the schema admits and what the console refuses; the plan
ladders card gains a Member position row; the kit table says the fixture
builder covers plan fixtures.
No output changes: only Markdown under docs/ is touched, no Go, SQL or
template file. git grep for member_products.go in docs and scripts is empty,
make lint and the notebook-citation check pass, and the lint snapshot differs
from the previous one by removed 0, added 0.
docs/testing.md: the front-matter summary names the test kit; the TL;DR runs
make test, and one package on the same footing after a reset. Database-gated
tests open through dbtest: Open skips when TEST_DATABASE_URL is unset, never
migrates and refuses a database whose core ledger is behind; OpenE2E and DSN;
Scratch, UpTo and ApplyVersion for migration subjects; Q and AwaitLockWait.
The self-migrating table and the hand-list warning are gone. The fixtures
paragraph describes world (console functions, fails on any error, suffixed
names, no cleanup, any *sql.DB) and states the exemption for a test of the
schema itself. Type 4 names the server harness's set helpers (OperatorSet,
MemberSet, Render), htmltest selectors by role and by aria-*, hx-* and data-*
attributes, and authtest. Type 7 gives browsertest's rules: boolean waits
only, ClickAndLand for a navigating answer, AwaitSwap for a swap, Recover in
each walkthrough, AppDB never migrates and skips without its key, free
functions taking t, and phases as named functions. A table lists the kit's
packages and what each may import. A paragraph describes proving a converted
assertion with a deliberate break and scripts/mutation-check. The complexity
bullet says a closure passed to t.Run counts toward the enclosing function.
The lint bullets say the lint parses each template in the set its surface
builds in production and fails on a file no surface parses, and that forbidigo
refuses rod's load and stable waits. The stale internal/workflows/stripe path
becomes internal/integrations/stripe/workflows, and the notes that described
TEST_DATABASE_URL as MC_DB_DSN, the shared-database fixture pollution and the
source-order convention now describe the separate test databases and the
reset.
docs/go-conventions.md: a transaction opens through db.InTx or db.Begin with a
kind (entitlements.Materializing or RuleChange for rendezvous transactions),
its begin and commit errors unwrapped; a pool lock goes through LockPools or
LockPoolOfGrant, and the order lives in database-locks.md; test helpers take
testing.TB and fail on error; "Where the checks live" names the rod wait rule,
the waits it refuses and those that pass it.
docs/database-locks.md: the pool row's "Taken by" names LockPools and
LockPoolOfGrant and their callers; Revoke, Manage, tier removal, default
change and the rule change say LockPools; the intro of the order section names
the two kinds as what sets lock_timeout, says a plain transaction sets none,
and says which paths render the lock refusal; rule 3 says "through LockPools";
the #186 bullet keeps only the advisory-key half.
test/AGENTS.md and test/reset-test-db.sh: the reset is the only migrator and
dbtest.Open refuses a stale ledger; the walkthroughs run before and after a
change from the demo snapshot.
docs/building-an-integration.md, docs/design-system.md and
docs/operator-ux-conventions.md: template sets come from web.MemberTemplates
and web.OperatorTemplates with the integration's web.Mount; operator files are
named <key>_operator*.html; no ParseUIPartials and no per-set surfaceRoot or
routeURL registration; the helpIcon row names forms.Help and forms.HelpIcon.
The DB-backed tests bullet of docs/building-an-integration.md says integration
tests open through dbtest.Open, the reset migrates every registered stream, and
a migration's own test runs on a dbtest.Scratch database; its UI-assets
paragraph says the member set panics at boot through template.Must, an operator
handler returns the constructor's error, and the lint fails on a file neither
set parses.
A survey of the twenty notebooks on disk found general facts that no
tracked page stated; the maintainer struck the weak rows and the rest
are written into their owning pages in each page's own words. The
design-system page gains the hidden-navigation figures that replaced a
dangling survey pointer, the reason the anatomy is parts and lint, the
record-table grouping and single-tint rules, and when a surface earns
row editing. The first-contact process gains a code-verification phase,
the transcript lane for models without vision, the three kinds of
honesty-audit drift and the click-path guardrail. The operator
conventions gain guards-before-writes, error state over empty state,
copy that states the real effect, toasts that report counts and the
PostForm rule. The Stripe, FedWiki, identifiers, IA, testing, model and
environment pages each gain their facts, the integration guide stops
telling authors to hand-write a page title, issues.md logs the
placeholder security contact, milestones.md records the Codeberg terms
clause and CONTRIBUTING.md notes that the module path is the forge URL.
One claimed fact was checked against the code and not written: the
mass-mutation preview does not run the commit loop in a rolled-back
transaction.
Extract ExtendGrantTx, RevokeGrantTx, and ExpireGrantTx into
internal/entitlements so the operator handlers, expiry activity, and
demo seed all run the same conferral path. The seed plays a five-grant
history on Carlos's org; pin-timestamps now uses dense-rank offsets
and transition list ordering ties break by transition_id.
Rebuild every operator, member, and integration template on the shared
page-anatomy parts, empty the lint allowlist, and move the confirm modal
into both shells. Replace back links with location trails and make row
identifiers the primary link. Make screen captures deterministic by
resetting the app database from a pinned demo snapshot. Send the logout
id_token_hint only while valid.
- Add shared ui_*.html parts (pageHeader, sectionHeader, statusBadge,
emptyState) parsed into every template set
- Add anatomy lint rules with a shrinking allowlist and screen-coverage
check
- Add make screens capture harness with contact sheets and baseline diff
- Compose member and FedWiki regions server-side so pages arrive
complete
- Rebuild Domains and Integrations on the parts as pilots
Sweep stranded pending claims at boot and on a Temporal schedule while
preserving evidence-based abandonment semantics.
Apply occupancy and name-policy checks to carves by operator-root owners
without affecting direct operator placements.
Run goose once per migration source with its own
goose_db_version_<name> table and native file numbering.
Remove positional namespace assembly, update rollback/status behavior,
and document the OpenSpec decision.
Rewrite the README around purpose, architecture, quickstart, and
status. Add contributing/security policies plus a docs index with
audience front matter across existing docs.
Document the kinds of tests (pure unit, DB-gated integration, Stripe-mocked,
HTTP handler/render, route/template lint, live e2e, OpenSpec validation), what
each covers, how to run them, and the role of the test/ stack directory. Add a
Testing subsection to the README pointing at it.