Repository navigation
Promote edge to stable: Polis 2.43.0 - #3321
Merged
Merged
Conversation
…und, so another process cannot answer them on macOS (#3278) Supertest 7 starts the app with listen(0), which binds the IPv6 wildcard, then sends its requests to 127.0.0.1 on the port it read back. macOS lets an IPv4 listener hold the same port number, so on a developer machine with other services in the ephemeral range a test request could be answered by a foreign process: an Express 404 from another app, a 502 from a local relay, or a socket hang-up. Linux refuses the shared port, so CI never saw it. The test harness now maps Supertest requests to the family the server actually bound, only on macOS and only when ::1 exists; globalSetup binds and requests on matching IPv4 addresses; four regression cases prove the failure and the fix. No product code or assertion changes. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…3279) Seventy files of comments, docstrings and documentation carried references to a private planning tracker: review round numbers, item numbers, plan and finding names, and working labels that meant nothing to a reader of this repository. Each is reworded to state the behaviour or the reason directly, or dropped where it only pointed at the tracker. Public pull request numbers stay. No code, string literal, test assertion, snapshot, migration or workflow step changes; files whose bytes are pinned by a checksum are left untouched. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…arting it (#3280) The CodeDeploy group for the web boxes moves to the WITH_TRAFFIC_CONTROL deployment style on the web target group: BlockTraffic deregisters a box and waits for the deregistration delay, the install hooks run, ValidateService passes, and AllowTraffic registers the box again after two healthy checks. Today a box is restarted while still receiving traffic, which costs a few dozen 502s per deploy. The math and Delphi boxes move to their own deployment group with no load balancer, deployed alongside the web one, and the deploy role may create deployments in both groups. The web target group checks health every 10 s with a 5 s timeout, two checks to become healthy and three to become unhealthy, and a 60 s deregistration delay. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…and Rust recognises the XID token kind (#3281) A Rust test mints anonymous, XID and standard-user participant tokens with the Node server's claim set and verifies them with the same options the server's express-jwt uses, against recordings made by the Node signer; a Jest test mints every kind with the real Node signer on each run, compares header and payload with the recording, and verifies Rust-minted tokens. Refusals are pinned on both sides: exp at now, wrong audience or issuer, future nbf, RS384, unsigned alg none, and HS256 signed with the public key. polis-auth gains the XID token kind for verification only, with the request-xid precedence rules the server applies, and a table test of request identities. Nothing is served or minted by Rust; no production key is involved. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ag, off by default (#3282) The inactive coordinator added eleven resources, an ingress rule on the database security group and two stack parameters with no default, so a deploy from a clean checkout stopped on the missing parameters and production was deployed from a hand-edited working copy instead. The coordinator is now synthesized only when -c enableCoordinatorInactive=true is passed. Without it the stack contains none of its resources, parameters or output, and a clean checkout synthesizes what production runs. With it the template is unchanged. Once a deploy has included the flag it must be passed on every later deploy, or the coordinator's resources are deleted; the documentation says so. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…d to it (#3283) * deploy: the Rust API service runs on the web boxes with nothing routed to it polis-api is added to the production compose profile on the web boxes and started after Node, nginx and the participation client, with no route selected and the nginx map unchanged, so nothing served changes. It connects to Postgres over verified TLS with the same pinned certificate bundle the other images carry, checked at build and at start; refuses to start without TLS; is capped at half a CPU and 512 MiB with eight read-only connections; and reports a health document with its version, commit, routing state and pool bounds. The deploy's validation reads that health: while no route is selected a failing service is logged and the deploy passes; once a route is selected it fails the deploy. Stopping the stack stops the service too, so a deploy with the service switched off or a rollback leaves nothing running. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * deploy tests: the fake docker login reads its password from stdin after_install.sh now runs with pipefail, so a fake docker login that exits without reading --password-stdin lets the fake aws ecr get-login-password die of SIGPIPE on the Linux runners, failing ten hook tests with exit 141. The real docker reads stdin; the fake now does too. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…pationInit's objects as the server serves them today (#3284) * polis-contracts: the participant token, the auth envelope and participationInit's objects as the server serves them today Three typed entries describe current server behaviour so the Rust side can be checked against it: the auth envelope the server injects into response bodies (which responses carry it, including error bodies; a falsy existing value replaced in place; expires_in fixed at one year), the participant JWT header and the three claim kinds exactly as the server mints them, and participationInit's ptpt, xInfo and conversation objects with their key order following the bootstrap schema's column order. The conversation entry names the supported legacy table layout and rejects it explicitly. A test decodes the recorded server-minted tokens and re-encodes them byte for byte, so a change in the server's claims surfaces here. Nothing served changes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-contracts: no expect in library code polis-api compiles schema.rs into its binary, where clippy denies expect(); literal_u32 now builds the JSON number directly, which cannot fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ly while the section is hidden (#3287) The old condition tested a jQuery selection object, which is truthy even when it contains no elements. At or above the minimum participant count, every person update on a visible section therefore scheduled a fade whose completion called initialization again. Changed dimensions could rebuild the visualization, register the person listener again, and request more comments through the immediate listener update. Check the selection length so only a hidden section takes this fade path. The test covers counts at and above the minimum: a hidden section fades and initializes, while a visible section schedules neither, even after a width change. It also verifies that the reveal's listener broadcast does not schedule another fade. Ordinary person updates still request comments; the visible section adds no request through re-initialization. Resize, tab, and initial reveal behavior are unchanged. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…nly probes for half-made identities (#3285) Thirty-five fixed-label counters record what the Rust side does with each request's identity (verified, refused, handed back to Node and why, admitted by membership), exposed as an identity object on the internal health document; nothing on the request path waits on them. Every route uses one table of hand-back reasons, and an admitted OIDC request on the votes read is no longer counted as a hand-back. Three read-only SQL probes count the half-made states the server's join path can leave behind: users created and never joined, participants in xid-required conversations whose user has no xid under the owner, and OIDC mappings without a user; each is counts only, bounded by a statement timeout, and tested against the migrations. Nothing served changes. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
… sides (#3286) Sixty recordings of the server's identity behaviour, made on the sealed characterization stack: the anonymous first vote, bare-xid first contact on participationInit, votes and comments with the allow-list off and on and with xid_required, XID tokens in their own and another conversation, the standard-user join, OIDC first use and repeat, Treevite accept and login, the permanent cookie, a refused first join that leaves a user with no participant, and a failed xid binding that leaves an unbindable participant; plus the refusals (wrong audience, wrong issuer, unsigned, wrong algorithm, expired). The recorder compares each run against the committed fixture and re-records only when asked; a Jest test replays the fixture against the live server; a Rust test verifies the twenty recorded tokens with the polis-auth verifier. A new workflow runs the same local entry point in hosted CI. Nothing served changes. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…min account (#3288) An external identity used to resolve directly to its linked user and could issue or reuse a participant token with that user's account privileges. Refuse XID authentication for anyone who owns a conversation, has the owner flag, moderates a site, is listed in ADMIN_UIDS, or has an OIDC mapping. Check both new credentials and existing XID tokens, including cookie conversion. Return HTTP 403 with polis_err_xid_owner_account and log the account class without identity values. Plain participants still resume their identities, and first contact still creates a participant account. Existing identity bindings are unchanged. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…the deployment (#3289) A failed polis-api image pull used to stop the deployment even when no Rust route was selected. Resolve routing and the start switch before pulling, catch that unrouted pull failure, log it once, and skip Rust startup so the Node deployment can finish. Selected Rust routes still require a successful pull. Migration and Node image pulls, invalid manifests, and other deployment failures stay fatal with their existing exit codes. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…r or admin account (#3290) * polis-auth: an external identity never resolves to an owner, moderator or admin account Check XID accounts against current primary database privileges before using verified claims. Match the Node refusal body and class order, and expose the same guard for admission and token issuance. Count refusals by class without logging identity values. Pin refusals and plain participation to Node recordings. Test the predicate against migrated PostgreSQL tables and recheck tokens after privilege changes. No routes, schema or Node behavior change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-api: the health identity-key test counts the event list instead of a literal Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…d never served (#3291) * polis-auth: participant admission as one transaction, switched off and never served Commit the user, participant, XID binding, invite consumption and prepared recovery code together. Lock the invite before admission and keep the existing participant-number lock until commit. Roll back failed attempts in full. Preserve anonymous conversation scope, owner bypass and the owner-account XID refusal. Keep bcrypt, token signing and child-invite issuance outside the participant-number lock. No route, Node handler or schema changes. Test concurrent invite and identity joins, failures at each write boundary, and shared admission results from the Node recordings. Format, clippy and the polis-auth tests pass through the shared CI entry point. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-auth: the admission recordings test passes the account check that verification now requires Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-auth: import the unavailable type from the crate root in the recordings test Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…es everything or nothing (#3292) * embed pages: page ids up to 2,000 characters, and a first visit creates everything or nothing Every visit to a page with an id over 100 characters returned a 500 and left an orphan empty conversation. Widen page_ids.page_id and page_ids.site_id to varchar(2000), keeping the unique site and page pair. Create the conversation, page mapping and invite in one transaction so all three succeed or all three roll back. When first visits race, roll back the losing insert and redirect both visitors to the winner. Reject missing, empty or overlong site and page ids with a clear 400 before writing anything, while preserving the existing URL parsing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * migrations: the first-deploy report includes the page id width migration The report builder lists the migrations that sit outside the adoption set; the new migration joins that list and the generated report is refreshed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * coordinator: the schema census includes the page id width migration Measured against a fresh PostgreSQL 17 with the whole migration chain applied: the two page_ids columns widen to 2,000 characters; nothing else changes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * migrations: the forward proof's additive ledger lists the page id width migration Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…t role (#3296) * comments: a participant's statement never looks up the owner's account role The old comment path made two external calls to the identity provider on every comment to find the conversation owner's account and read its role. Remove the management client, the owner-role helper, the remaining Pro-gated comment-list branch, and the unused management package. Moderation emails still use the account email stored in the database, and comments still schedule notification tasks. Login configuration is unchanged. The report's moderation level filter, which only applied when the owner carried a paid role, now applies to every conversation; no account is looked up for it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * comments: the moderation-list test expects the level filter to apply without an owner lookup Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
The post-comments recordings guard treated every file under server/characterization as a recording. The route inventory and its scope are a census of the registrations the code declares, and the identity recordings map observed route hits through that inventory, so a route removal has to update it in the same change or every identity case misreports its route hits. The inventory and scope files are exempted; recordings, cases, fixtures and comparison rules stay guarded. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…wo forms as Node (#3299) * polis-api: participant signing keys read once at start, in the same two forms as Node Load and validate the RSA pair before startup. Use the public handle for participant verification and keep a private handle for future signing. Report loaded status and a public-key fingerprint in health without key bytes. Cover value and path settings, escaped newlines, boot refusals, and Node signature and fingerprint agreement. No token issuance or route changes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-api: absent signing keys are reported, not fatal, unless a participant route is selected A box that routes no participant traffic to the service (and every fixture that starts it without keys) boots and reports participantKeysLoaded false; a present but malformed, unreadable or mismatched pair still refuses to boot, and so does a selected participant route without keys. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-api: the public key alone is enough to verify, and the earlier public-key path setting is accepted A process that only verifies participant tokens needs no private key; one that is configured must be valid and must match. The fixtures and the deploy set POLIS_API_PARTICIPANT_PUBLIC_KEY_FILE, which now stands in for JWT_PUBLIC_KEY_PATH when neither JWT_PUBLIC_KEY form is set. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
… shared recorder modules (#3303) * ci: the post-comments recordings guard covers its own profile and the shared recorder modules The guard treated every file under server/characterization as a post-comments recording input, so a server change that must update another profile's cases (identity, votes, collective-statement, jobviews) in the same pull request could never pass, although each of those profiles has its own replay workflow and re-record procedure. The guard now covers the post-comments directory and the shared recorder modules at the top of the tree, which is what its header describes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * ci: the route inventory tool and its unit test are census files too Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
… keys refreshed off the request path (#3297) * polis-auth: participant admission as one transaction, switched off and never served Commit the user, participant, XID binding, invite consumption and prepared recovery code together. Lock the invite before admission and keep the existing participant-number lock until commit. Roll back failed attempts in full. Preserve anonymous conversation scope, owner bypass and the owner-account XID refusal. Keep bcrypt, token signing and child-invite issuance outside the participant-number lock. No route, Node handler or schema changes. Test concurrent invite and identity joins, failures at each write boundary, and shared admission results from the Node recordings. Format, clippy and the polis-auth tests pass through the shared CI entry point. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-auth: the admission recordings test passes the account check that verification now requires Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-auth: import the unavailable type from the crate root in the recordings test Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-auth: single-sign-on first use as one transaction, and provider keys refreshed off the request path Provision verified provider identities with the existing email and name rules, owner defaults, and social-login mapping replacement. Roll back the whole attempt on uniqueness races and reuse the bounded admission transaction policy. Load provider keys from a boot snapshot and refresh them in a separate task. Unknown keys refuse immediately; refresh attempts are rate limited, failures retain the previous keys, and identity counters expose misses and refreshes. Add migrated PostgreSQL, Node recording, and local provider rotation tests. Keep the new functions unrouted and disabled. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-auth: the provisioning recording pins the current Node source The recorded file gained a connection parameter on its join-code helper when the page id change merged; the single-sign-on provisioning path it records did not change, so the pin moves to the current bytes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ff (#3301) Build the Rust version response with the same script and inputs as Node. Keep its JSON bytes, unknown-provenance fallback and no-store header. Add separate process and nginx switches, with both off by default, and compare version responses in the deploy health check when selected. Record the Node contract and test image parity, HEAD, conditional requests and routing fallback. The route does no database or participant work. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ptance, switched off and never served (#3304) * polis-api: POST /api/v3/votes for admitted participants, durable acceptance, switched off and never served Add the reviewed vote handler for existing conversation-bound participants. Keep first contact, new participants, bare XIDs and cookie recovery on Node. The independent switch defaults off; nginx routing is unchanged. Return the stored vote with HTTP 200 when star or next-comment work fails. Report follow-on degradation in the body. Decide the same-choice retry rule in the handler's Rust transaction: lock the latest vote key, compare the stored choice and append only on change. No migration or new Postgres function is needed. Report uncertain commits separately and never hand a dispatched vote to Node. Preserve independent bounded pools, capped best-effort notifications and parser hand-back. Add health counters, explicit pre-dispatch markers, current Node witnesses and database/replay tests. Node keeps its deliberate append-on-every-request behavior in the contracts. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-api: the health field count includes the votes handler's entry after the merge Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…me (#3305) A live vote or comment can return before its background interaction write finishes. The tests sometimes read the old interaction timestamp in their baseline and the new one after the import correctly refused the job. Wait for the real interaction writes before taking that baseline or returning the downloaded body to the import worker. Settle pending writes on failures, and await closure of the application pools so Jest's worker can exit. The tests still prove that occupied targets refuse imports without changing existing rows, live votes survive in history and latest, and zero-batch jobs refuse a vote arriving during download without completion side effects. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…replays (#3306) * characterization: Treevite recordings, every route and refusal, both replays Record all nine Treevite routes, authentication and validation refusals, login round trips, child issuance, identity conversion and a real database race. Preserve the current partial state as an explicit security delta. Keep generated login codes in memory and compare normalized wire bytes and committed rows against the pinned fixture in two fresh replays. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * characterization: the Treevite recorder names the fields it discards Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…3307) * polis-auth: participant admission as one transaction, switched off and never served Commit the user, participant, XID binding, invite consumption and prepared recovery code together. Lock the invite before admission and keep the existing participant-number lock until commit. Roll back failed attempts in full. Preserve anonymous conversation scope, owner bypass and the owner-account XID refusal. Keep bcrypt, token signing and child-invite issuance outside the participant-number lock. No route, Node handler or schema changes. Test concurrent invite and identity joins, failures at each write boundary, and shared admission results from the Node recordings. Format, clippy and the polis-auth tests pass through the shared CI entry point. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-auth: the admission recordings test passes the account check that verification now requires Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-auth: import the unavailable type from the crate root in the recordings test Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-auth: single-sign-on first use as one transaction, and provider keys refreshed off the request path Provision verified provider identities with the existing email and name rules, owner defaults, and social-login mapping replacement. Roll back the whole attempt on uniqueness races and reuse the bounded admission transaction policy. Load provider keys from a boot snapshot and refresh them in a separate task. Unknown keys refuse immediately; refresh attempts are rate limited, failures retain the previous keys, and identity counters expose misses and refreshes. Add migrated PostgreSQL, Node recording, and local provider rotation tests. Keep the new functions unrouted and disabled. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-auth: the provisioning recording pins the current Node source The recorded file gained a connection parameter on its join-code helper when the page id change merged; the single-sign-on provisioning path it records did not change, so the pin moves to the current bytes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * identity: Node and Rust admitting participants together, under load Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
… directly (#3302) Keep the existing API and page routes on their selected backends. Send known assets and public documents directly to the file-server, preserving their bytes, cache headers and compression. Return the file-server-compatible error body locally for unmatched paths. Keep the application catch-all registered while page routes still use it. Cover route selection, static files, error bodies and the absence of unknown requests in the application log. Verify reloads with concurrent participation traffic and save both rendered route selections. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* retire: the spam-check client and the permanent-cookie recovery The spam check had not been on the comment path for some time. A source search confirms that Akismet only constructed its client and verified its key at boot. Remove that startup call, dependency, setting, and references. The permanent-cookie recovery had not recovered a participant in a month of production logs. A request with only the old pc cookie and no token now follows the normal first-contact flow and joins as a fresh participant. Keep the database columns and replace the recovery tests with a regression that checks the fresh participant and its signed token. No other code reads req.cookies, so remove the cookie parser too. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: the admission model, identity recordings and replay pins follow the cookieless server Treat a request with only an old cookie as first contact. Keep both cookie entries in the admission matrix: no external ID follows anonymous entry, and a denied external ID follows new denied-ID entry. Keep both identity cookie cases and record fresh users and participants. The XID case now creates a binding to the new token's user. Preserve JWT verification and the fixture's source hashes. The collective-statement index changes only egress.refusedAtServerLoad: rest.akismet.com:80 is gone because the retired spam-check client no longer verifies its key at startup. All 51 case recordings are unchanged. Two fresh recordings are identical and replay matches byte for byte. Validation on disposable Docker services: all 2,400 admission cases match, all six setup unit tests pass, both identity replays match all 60 cases, and all 123 identity and token tests pass. Both route-inventory hashes still match the source. No runtime or participation-path code changes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: the collective-statement egress pin is re-recorded in its own change after this merges The replay index records which hosts the server tried to reach at load; with the spam-check client gone that list is empty. The recordings guard requires that pin to move in its own change from edge, so it stays as recorded here and follows in a one-line re-record. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…y registration index (#3308) Removing an earlier route shifts the observer's registration indexes even when Treevite behavior has not changed. Map hits through the enabled route inventory to stable route ids, remove duplicates, and sort numerically. Rewrite the fixture by mapping its existing indexes through the inventory, not by re-recording. Preserve every other byte of the fixture. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…m-check client's removal (#3309) Re-record the collective-statement profile after the spam-check client was removed. The server no longer attempts its load-time key check, so the load-time egress refusal list is empty. Two recordings are identical and two fresh replays match all 51 cases. Every case file and hash is unchanged; only the egress pin moves. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* retire: the tutorial, onboarding and marketing pages Remove POST /api/v3/tutorial and the unused page shells and redirects. No successful request since July 7: the tutorial write, /bot/install*, /bot/support*, /pwreset*, /other-conversations*, /user/create*, /inboxApiTest*, /hk/new*, /s, /s/CTE and /nabi. Some of these registrations were shadowed by broader page routes. Remove the remaining onboarding shells: /createuser*, /user/login*, /user/logout*, /settings*, /conversation/create*, /pwresetinit*, /inbox*, /hk* and /s/*. Remove the marketing and demo pages: /gov*, /company, /contrib*, /demo, /demo/<id> and /bot*. These served successful requests in the September 26 through October 25 access-log window. The aggregate check does not retain their exact last-success timestamps. Remove /about, /football and /pdf redirects. All three returned redirects in that same window; exact last-success timestamps are not in the check. Remove the tutorial CLI operation and docs, legacy demo router entry and analytics mapping, dead tutorial store method, unused page wrappers and redirect helper. Remove the seven matching Rust page handlers, contracts, read settings, nginx switches, health counters and obsolete test fixtures. Retire the 29 stable route identities and check their 404 responses through the real static-file proxy and the generic API fallback. Keep /home, /privacy, /tos, /donate, /signin and /signout. /feeds/*, /api/v3/votes/famous, /polis_site_id*, /report_bundle*, /report_style* and the six topicMod routes stay. No table or column changes; vote, comment and next-statement work is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: the retired-page test accepts the proxy's 502 where no file server runs, and the differential harness no longer expects the tutorial step Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: the characterization inventory moves to its own change from edge The recordings guard requires the route inventory, its scope and its cases to change in a separate pull request from the code they describe; those updates follow once this merges. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: the route inventory and its scope move with the code again The recordings guard now treats the inventory and scope as the census they are, so the retired entries, scope and cases return to this change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-api: the health field count follows the retired page routes after the merge Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…tly backups; the upgrade and restore recipe (#3312) * self-hosting: a rebuild never deletes a database without a dump; nightly backups; the upgrade and restore recipe Check the selected database in an isolated copy before removing any project volumes. Refuse populated databases unless data loss is explicitly confirmed, and save a verified host dump before proceeding. Keep the backups volume and volumes belonging to other projects. Add dump and restore commands, nightly dumps with fourteen-file retention, and a startup refusal for legacy database volumes. Document upgrades and restores, including the PostgreSQL 13 to 17 transition. Keep these services out of the production composition. Exercise refusal, dump failure, restore failure, round-trip row counts, password migration, retention, connection cleanup and rebuilds on real PostgreSQL and Compose containers. Review round 2 asked for three changes, applied here: host dump files under ./backups/ are ignored by git; the guide creates ./backups before copying a nightly dump out; the test script's negated checks are explicit failures (a negated pipeline is exempt from errexit). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * self-hosting: the three review fixes (ignored dumps, the backups directory, explicit test failures) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…not on time (#3315) Hold append, notification and star work at explicit database gates, and observe commit and pool completion before checking durable rows. Attribute dispatch creation to each concurrent caller so only a verified pre-dispatch handback may be retried. Keep a real sixteen-caller first-append race with bounded, reported retries of entirely empty rounds. Keep eight narrow readers and both real read limits while all four writer slots stay held. Retry only read-pool acquisition timeouts, at most three attempts per reader, with unchanged writer evidence required on every attempt. Keep the held-append response between the real dispatch deadline and that deadline plus 500 ms. Observe vote and notification commits independently of the star gate and retain the 600 ms notification promptness check. Warm fixture connections, retain concurrent notification pressure, and cancel an outstanding route when checking detached vote durability. The production handler, pools, deadlines, vote conversion and fallback behavior are unchanged. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…off (#3311) * polis-api: Treevite wave creation as one typed transaction, switched off Create the wave and its root and member invitations in one transaction. Serialize wave numbers and owner-wide capacity checks with advisory locks. Keep Node response shapes and member top-up behavior, with overridable code ceilings checked before inserts and separate bounded minting resources. Add recording replay and database tests for numbering races, quota refusals, collision retries, rollback and participation isolation. Keep routing off. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-api: a wave create fills lost child allocations of earlier waves After issuing the new wave's codes, the same transaction looks for members of earlier child waves whose parent invite was consumed but whose own allocation is short, and fills the deficit under the existing per-(wave, member) advisory lock, counting every code state first so no member is granted twice. At most 100 deficient allocations are filled per create; the rest wait for the next create or accept. The response still reports only the new wave's codes, as Node's does; repairs are logged as a count. A repair failure rolls back the new wave. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…d off (#3313) * polis-api: Treevite wave creation as one typed transaction, switched off Create the wave and its root and member invitations in one transaction. Serialize wave numbers and owner-wide capacity checks with advisory locks. Keep Node response shapes and member top-up behavior, with overridable code ceilings checked before inserts and separate bounded minting resources. Add recording replay and database tests for numbering races, quota refusals, collision retries, rollback and participation isolation. Keep routing off. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-api: a wave create fills lost child allocations of earlier waves After issuing the new wave's codes, the same transaction looks for members of earlier child waves whose parent invite was consumed but whose own allocation is short, and fills the deficit under the existing per-(wave, member) advisory lock, counting every code state first so no member is granted twice. At most 100 deficient allocations are filled per create; the rest wait for the next create or accept. The response still reports only the new wave's codes, as Node's does; repairs are logged as a count. A repair failure rolls back the new wave. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-api: Treevite accept and login as typed write handlers, switched off Add independent admission and crypto capacity for invitation acceptance and return-code login. Keep expensive work outside the admission transaction and retain durable acceptance when follow-on work is delayed. Enforce invitation age, login-code expiry, anonymous identity boundaries, and second-invite burning without replacing a live recovery credential. Sign new participant sessions for one day and top up child-wave invitations idempotently. Keep the new route switch disabled and leave gateway routing unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * ci: the polis-api test step names the admission fixture's database; the replay harness drops an unused argument The Treevite write tests reuse the admission fixture from polis-auth, which reads POLIS_AUTH_TEST_DATABASE_URL. The polis-api test step only set its own variable, so those tests panicked on the hosted runner while passing on any host that had both. Point the fixture at the same disposable database; it creates its own schema per process. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ched off (#3314) GET /api/v3/comments/participant returns, for one conversation, the two fields the participation page reads from the statement list: txt and tid, in Node's property order. Visibility is exactly Node's plain participant list (active, the conversation's moderation rule, positive velocity, created order, the default cap); no query key can widen it. Identity is the comments twin's hybrid resolution: anonymous allowed, cookies, malformed and foreign-conversation tokens refused. The route has its own one-slot read and identity pools with short bounds and makes no writes. It is behind its own switch, off by default, and has no nginx map entry, so nothing is served by this change. Recordings against unchanged Node compare the whole projected body: 426 of 426 over 71 conversations are byte-identical; the topic agenda's expanded call is listed in deltas with its reason. The boot health test counts two new fields. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…helpers, old bundles and embed demos (#3294) * retire: the share, summary, explore and one-time pages, the implicit helpers, old bundles and embed demos Remove GET /share/<id>, /summary/<id>, /explore/<id>, /ot/<id>, /iip/:conversation_id and /iim/:conversation_id. Each has no successful request since July 7 in the access-log check through October 26. Remove /embed, /embedPreprod, /embedReport, /embedReportPreprod and their .html files, plus /styleguide and /dist/admin_bundle.js. The preproduction and report demos, styleguide and old admin bundle have no successful request since July 7. /embed had five 200 responses in the July 7 to October 26 window; the check did not record their latest timestamp. /embed.html had requests in that window; its last successful timestamp was not recorded separately. Remove /__webpack_hmr, /cached/*, GET and ALL /font/*, and the old /*embed*js/* proxy registration. The hot-reload endpoint had two 200 responses during the final 30 days of that window; the check did not record their latest timestamp. The other three paths have no successful request since July 7. Remove the unreachable catch-all proxy registration and the never-enabled missing-file branch, which had no calls. Remove the six corresponding Rust routing switches, nginx templates, link handlers, dedicated pools, counters, contracts and conformance cases. Drop their process settings and the legacy one-time-link router entry. Move the nineteen registrations to the retired inventory and verify the retired pages and demo filenames return 404 through the remaining proxy. Keep feeds, famous votes, site-id integrations, the live embed.js, conversation pages, home, privacy, terms, donate, signin, signout, report bundles and styles, all six topic moderation routes, and every table and column. The participation path gains no work, locks or waits. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: the retired-page test has its own file name and accepts the proxy's 502 where no file server runs Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: the characterization inventory moves to its own change from edge The recordings guard requires the route inventory, its scope and its cases to change in a separate pull request from the code they describe; those updates follow once this merges. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: the route inventory and its scope move with the code again The recordings guard now treats the inventory and scope as the census they are, so the retired entries, scope and cases return to this change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-api: the health field count follows the retired page routes and the key custody fields Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: drop the file fetcher and static-port settings the share-bundle retirement orphaned Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…3295) * retire: the share, summary, explore and one-time pages, the implicit helpers, old bundles and embed demos Remove GET /share/<id>, /summary/<id>, /explore/<id>, /ot/<id>, /iip/:conversation_id and /iim/:conversation_id. Each has no successful request since July 7 in the access-log check through October 26. Remove /embed, /embedPreprod, /embedReport, /embedReportPreprod and their .html files, plus /styleguide and /dist/admin_bundle.js. The preproduction and report demos, styleguide and old admin bundle have no successful request since July 7. /embed had five 200 responses in the July 7 to October 26 window; the check did not record their latest timestamp. /embed.html had requests in that window; its last successful timestamp was not recorded separately. Remove /__webpack_hmr, /cached/*, GET and ALL /font/*, and the old /*embed*js/* proxy registration. The hot-reload endpoint had two 200 responses during the final 30 days of that window; the check did not record their latest timestamp. The other three paths have no successful request since July 7. Remove the unreachable catch-all proxy registration and the never-enabled missing-file branch, which had no calls. Remove the six corresponding Rust routing switches, nginx templates, link handlers, dedicated pools, counters, contracts and conformance cases. Drop their process settings and the legacy one-time-link router entry. Move the nineteen registrations to the retired inventory and verify the retired pages and demo filenames return 404 through the remaining proxy. Keep feeds, famous votes, site-id integrations, the live embed.js, conversation pages, home, privacy, terms, donate, signin, signout, report bundles and styles, all six topic moderation routes, and every table and column. The participation path gains no work, locks or waits. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: the dormant API routes, the unused dependencies and settings Remove users/invite, snapshot, metrics, sendEmailExportReady, math/correlationMatrix, mathUpdate, contributors, auth/deregister, dataExport, dataExport/results and math/pca. These paths had no successful request since July 7 in the retained access logs. Remove notifyTeam, whose last successful call was September 21. Remove dummyButton, which had two successful calls during September 26 through October 26; the access-log summary does not record their exact timestamps. Remove the matching CLI operations and documentation, the legacy button and metrics callers, report correlation fetching, and the Rust legacyPca handler, contract and routing switch. Mark all thirteen routes retired in the characterization inventory and replace their cases with a shared 404 test. Unknown API GETs return 404 without going to the static-file proxy. Remove aws-sdk v2 and the unused Mailgun, nodemailer, oauth, simple-oauth2, request, googleapis, aiplatform, vertexai, mimelib, nan and p3p direct packages and their unused type packages. Remove the periodic export test, comment-language backfill and inactive PCA polling loop, together with WEBSERVER_USERNAME, WEBSERVER_PASS, RUN_PERIODIC_EXPORT_TESTS, ADMIN_EMAIL_DATA_EXPORT_TEST, BACKFILL_COMMENT_LANG_DETECTION and the unused Mailgun settings. Keep axios, dd-trace and ENCRYPTION_PASSWORD_00001 because they still have readers. Feeds, famous votes, site-id integration, live pages, report assets, topic moderation, database tables and columns stay in place. Votes, comments and next-statement handling gain no new work or locks. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: the retired-page test has its own file name and accepts the proxy's 502 where no file server runs Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-cli: the math help test no longer expects the retired correlation and refresh leaves Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: the characterization inventory moves to its own change from edge The recordings guard requires the route inventory, its scope and its cases to change in a separate pull request from the code they describe; those updates follow once this merges. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: the characterization inventory moves to its own change from edge The recordings guard requires the route inventory, its scope and its cases to change in a separate pull request from the code they describe; those updates follow once this merges. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: the route inventory and its scope move with the code again The recordings guard now treats the inventory and scope as the census they are, so the retired entries, scope and cases return to this change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: the route inventory and its scope move with the code again The recordings guard now treats the inventory and scope as the census they are, so the retired entries, scope and cases return to this change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: leave the collective-statement harness untouched; its two stale settings go with the next re-record Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-api: the health field count follows the retired page routes and the key custody fields Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: the CLI command floor and the importance check follow the retired operations Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * Merge edge into retire/dormant-api; the health field count follows the retired legacy pca switch Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: leave the characterization harness settings in place; they go with the next re-record Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * retire: drop the file fetcher and static-port settings the share-bundle retirement orphaned Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * --- Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ng 202 and writing nothing (#3319) An admitted vote keeps its place in the writer queue after the response has returned. At 60 seconds, log the vote identifiers and count a writer acquisition alarm, then keep waiting on the same acquisition until a slot is available. The alarm never discards the vote and there is no wait ceiling. Reads and admission keep their 100 ms acquisition budget. Pool capacity, connection and statement limits, and the one-second response deadline stay unchanged. A waiting dispatch holds no database connection or blocking worker. Hold all four writer slots past a test alarm and prove that the dispatch survives, appends once, publishes its receipt and schedules notification. Require each concurrent caller's own completion and durable receipt before accepting 202. Preserve replay integrity and notification scheduling. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…go builds are locked (#3317) * deps: installs run no package scripts, lockfiles carry integrity, cargo builds are locked Use frozen npm installs with lifecycle scripts disabled at local lockfile-backed build and test entry points. Install the pinned Cypress browser explicitly where end-to-end tests need it. Add registry URLs and integrity to the legacy participation lock without changing package versions. Refuse missing Anthropic SDK at runtime and direct environment builders to the existing pinned requirements. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * ci: the coordinator workflow's server install keeps the prefix before the flag, as its campaign test asserts Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * deps: the routing recordings workflow keeps its install line; that change moves to its own pull request The recording rules and product code may not change in one pull request (the routing guard enforces it). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ackage scripts disabled (#3318) The same install hygiene as the rest of the repository, in its own pull request because the routing guard forbids changing the recording rules together with product code. Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…hed off (#3316) * polis-api: the participant statement list as a typed read route, switched off GET /api/v3/comments/participant returns, for one conversation, the two fields the participation page reads from the statement list: txt and tid, in Node's property order. Visibility is exactly Node's plain participant list (active, the conversation's moderation rule, positive velocity, created order, the default cap); no query key can widen it. Identity is the comments twin's hybrid resolution: anonymous allowed, cookies, malformed and foreign-conversation tokens refused. The route has its own one-slot read and identity pools with short bounds and makes no writes. It is behind its own switch, off by default, and has no nginx map entry, so nothing is served by this change. Recordings against unchanged Node compare the whole projected body: 426 of 426 over 71 conversations are byte-identical; the topic agenda's expanded call is listed in deltas with its reason. The boot health test counts two new fields. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-api: the moderation statement list as a typed read route, switched off Add a moderator-only statement list with the fields and pagination read by the moderation page. Verify authority before reading statements or counts. Use independent bounded read-only pools and leave the route disabled. Parse numeric query values like Node, preserve large offsets, and return its Bad Request response for malformed or repeated parameters. Compare every query and refusal probe against Node and retain reasons for deliberate differences. Require a limit and one moderation state so omitted values hand back before any database read. Keep participation pools and the next-statement behavior intact. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-api: the health census is 138 fields with the moderation list route The merge resolution counted the participant list's two fields twice: once from edge and once from this branch's history. Edge reports 136; this route adds two. --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…napshot that ends sessions within a minute (#3310) * polis-api: Treevite login-code revoke and reissue, and a revocation snapshot that ends sessions within a minute Add explicit owner and site moderator actions to revoke or replace a participant's login code. Keep the routes off by default and prepare replacement credentials on a bounded crypto worker before the write transaction. Load revoked participants at startup and refresh them every thirty seconds on a separate read connection. Check signed participant sessions against the in-memory snapshot and return a terminal unauthorized response so a fallback cannot bypass revocation. Add coverage for legacy year-long tokens, authorization, replacement credential derivations, failed refresh retention, and vote statement counts. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx * polis-api tests: the statement-counting proxy in the revocation test disables Nagle on both sockets The proxy forwards each PostgreSQL protocol header and body as separate writes. With Nagle and delayed acknowledgements on Linux, those small writes stall long enough to spend the vote's real 250 ms admission deadline, and the vote answered 502 inside the test only. Every assertion and all production code are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ss9NfEMZS9Qm4FZ2npXDrx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Contributor
Author
Delphi Coverage Report
|
colinmegill
marked this pull request as ready for review
November 2, 2026 22:27
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release plan
Version: 2.43.0
Reason: new selected migrations
Previous stable:
c341a0e715e87f153785b46f478fcf4fdd785403Candidate:
44f8b560395e40a6e960f9653745fe35ef2130e0release.txt additions:
000045_page_ids_width.sqlMerge the reviewed version preparation into edge, then refresh this plan for the resulting edge SHA before merging the promotion with a merge commit.