* Add typed mutation receipts and preserve pending write errors * Add canonical page revisions, snapshot reads, and composable transaction guards * fix: add portable native writer locks and runtime gates * Route PGLite operations through bounded local persistence IPC * Add durable mutation admission and recoverable canonical page publication * Prepare CLI mutations before connecting and preserve replay intent * Wire resident writer ingress and fence unmanaged canonical mutation * Fence derived projections by canonical revision and persist withdrawal rebuild work * Expose principal-scoped durable write receipts without widening grants * fix: retain datastore ownership through shutdown and fence lease cleanup * Journal memory verbs and publish semantic fact updates under canonical guards * Coordinate semantic page mutations and seal only current sanitized projections * Administer resident writers through authenticated local CLI IPC * Journal takes mutations and share long-hold budgets across physical pools * Route take mutations through replayable operations before opening PGLite * Compose canonical projections with publication and retry transient database contention * Document concurrent write guarantees and explicit receipt grant migration * test: exercise durable persistence schedules and process crash recovery * Apply shared persistence limits and compact permanent receipts with exact accounting * fix: honor explicit direct routes for generic Postgres poolers * Retain original page visibility ceilings and intentional sandbox persistence * test: verify pooler capacity and owner incarnation fencing * Preserve canonical projection round trips and timeline detail through recovery * Wire persistence distribution guards and refresh public operation documentation * Retry postcommit effects and recover withdrawal mirrors independently * Include outbox schema in snapshot identities and isolate fresh test brains * Fence legacy canonical writers and require revision-safe imports * Activate managed persistence only after explicit quiescence and durable fencing * Require honest median-of-three read latency under public writer load * Require writer progress during read latency samples * Compose graph reconciliation with canonical publication and retain writer ceilings * Exercise source-scoped takes and duplicate fences with durable principals * Preserve transaction receivers when restoring extraction test stubs * Install revision-bound retrieval fixtures and preserve strict projection gates * Keep engine test overrides composable across transaction clones * Assert atomic readback rollback and public failed write receipts * Settle definite publication failures and preserve typed receipts across CLI boundaries * Restore bounded fact extraction receipts and durable postpublication scheduling * Share activation fixtures within explicit engine lifecycles * Model scoped read transactions and publish complete retrieval fixtures * Coordinate resumable owner-scoped Markdown sync through durable page receipts * Exercise page source and privacy boundaries with durable writer fixtures * Publish complete search fixtures and assert composable scoped SQL routing * fix: retain archived alias reads and expose writer ingress diagnostics * Exercise managed writer activation in persistence validation workloads * Publish revision-bound ranker and max-pool retrieval fixtures * test: bind engine fixtures to coherent page projections and durable writes * fix: compare enrichment against its original page snapshot * fix: preserve withdrawal markers across legacy history and projection rebuilds * fix: authenticate resident sync ingress and prevent writer starvation * fix: durably acknowledge recovery and advance missing-file withdrawal effects * Retain mutation identity and revision through internal caller retries * Preserve contextual wrapping for deferred coordinated embeddings * Install coherent retrieval fixtures and inspect raw chunk state explicitly * fix: budget and fairly schedule standalone publication recovery * Keep privacy and image fixtures aligned with projection visibility * Bind embedding provenance to the prepared provider context * Publish complete relational and search regression fixtures * Preserve raw registry write checks and seal deferred embedding fixtures * Retry coordinated imports when source policy cannot be read * Preserve legacy migration data and exercise revision-aware public writers * Coordinate managed source lifecycle and durable clone recovery * fix: bind owner identity to the physical canonical checkout * fix: reserve control capacity during source lifecycle publication * fix: preserve delegated sync recovery and deferred embed ownership * Verify lifecycle staging identity and preserve bounded recovery * fix: account for pending clone requests in shared admission quotas * test: align retrieval and replay fixtures with durable publication * Release clone reservations exactly once and recheck source routing * Require PostgreSQL lifecycle and recovery contracts in CI * test: require physical-root refusal in the deployment matrix * Route source administration through resident ownership and retained identities * fix: retain captured canonical file routing under registered ownership * test: exercise authenticated atomic take publication and retained replay IDs * Retry confirmed admission aborts and preserve direct CLI conflict receipts * Fix revision-safe import revival and preserve identity deduplication * test: publish embedding migration fixtures before re-embedding * Exercise durable publication with the actual release executable * Exercise conditional embedding updates with coherent serial fixtures * Verify clean resident EOF shutdown in the release smoke * Seal search fixtures and assert durable side-effect outcomes * Bound recovery preparation and exercise admission timeout receipts * fix: retain delegated ownership and enforce take receipt holder grants * Restore tombstoned pages consistently and preserve unexpected local files * Preserve deletion state in canonical page versions * Retry withdrawals after releasing the complete transaction * Keep canonical version deletion schema bootstraps consistent * Keep canonical version types beside page state * Filter protected timeline content from remote version history * Require PostgreSQL version history privacy coverage * Require explicit native test timeouts in every CI entry point * Bind Windows native locks to the active runtime API * Test fact authorization with durable source grants * Wait for authenticated persistence readiness in compiled CLI smoke * Seal search fixtures against their canonical page revisions * Refresh managed lifecycle fixtures and reviewed canonical writer inventory * Measure staging bytes in clone metadata quota regression * Preserve canonical write provenance and typed storage failures * Measure durable admission through stable warmed workload instrumentation * Require both engines in durable admission instrumentation regression * Preserve bounded soak failure diagnostics and test fixtures * Exercise timeout preflight in local CI execution fixtures * Verify tombstone restoration values and stable no-op bytes * Keep native workflow coverage checks compatible with explicit timeouts * fix: bind local IPC safely across long paths and concurrent owners * Require IPC ownership and path tests on every native target * fix: preserve inaccessible IPC owners when Bun loses connect errno * Isolate PgBouncer fixtures from test database guards and shard resets * ci: isolate native-only dispatches from full persistence validation * Keep agent seed worktrees outside their coordination state * Seal expert search fixtures at their canonical page revision * test: exercise durable PostgreSQL take publication and replay * fix: retain native ownership across Windows IPC listeners * fix: serve admin SPA from hidden checkout directories * fix: derive Windows pipe claims from NT Unicode upcasing * test: match Unicode IPC aliases to actual Windows pipe equality The four Windows runners in run35037789501 proved dotless-i and I are separate pipe names: kernel_alias=false, CompareStringOrdinal=3, with identical NT and invariant-locale case tables. Keep both independent providers in coverage and require genuine sigma/accent aliases to share one live provider and native claim. Preserve the native API oracle. Restore the exact native source, build recipe, manifest and artifacts from 2a703bb9875f214708d0c23b070fdd188ed3b459; the provisional NT upcase change was unnecessary. All six non-Windows artifacts remain byte-identical. Input SHA256: before b86ba93886091ab6b8dbba48c891966680ed3484b5d815ac31684da401f1ec73 after 4759bdd927c0c0fa29c2d5af30e42972a183f9be006d595144d73b6ca1111636 Windows x64 SHA256: before f5c9d45e261757b510979534b447bb118e95d5699c3dd6e8f1ec31200c6372aa after 0b494ec94896219a3b142b6f216bf12c9ee94d27fa7e7399bcdab707ca60212e Windows ARM64 SHA256: before 74fe0d4e8205738675ba5dcccf55edbf5bb250ec96724b84b3490c30e3e27bf2 after e8315b4453dce3df775eeb76c73e8895c273390e64b16a2c255e10790fa75dd7 * Repair merged fixtures for revision-bound chunk reads * Require local legacy-chunk reads to honor projection seals * Account for reviewed upstream canonical write references * Isolate ambient keyset fixture page clocks from fact writes * fix: settle publication failures beneath blocked file ancestors * Match TTL vector fixture to the initialized fact column * Record coordinated purge in canonical writer census * docs: align concurrent persistence contracts and sync guidance * fix: fence projection replacement to its originating snapshot * fix: bound unit shard processes without dropping coverage * fix: publish embedding vectors and contextual completion atomically * test: isolate thin-client health and write fixtures * test: isolate healthy doctor checks from retained E2E grants * test: isolate each local unit file in its own Bun process * fix: keep persistence parameter schemas free of registry cycles * test: restore thin-client routing exit state * style: consolidate page operation engine imports * fix: isolate purge validator from generic flag scans * test: isolate Postgres bootstrap ownership fixture * test: isolate open-loop engine parity database * chore: bump version and changelog (v0.51.0.0) Document the coordinated concurrent-writer upgrade and synchronize the release manifests, migration guide, generated stamps and skills lock. Co-Authored-By: Codex <noreply@openai.com> * docs: update concurrent-write guide for v0.51.0.0 * fix: resolve concurrency persistence security scan findings --------- Co-authored-by: Codex <noreply@openai.com>
18 KiB
Concurrent writes and durable receipts
Each accepted mutation has a durable request UUID scoped to one brain and one authenticated principal. A response distinguishes acceptance from commitment. Keep the UUID and original arguments until the request reaches a terminal state.
Say to your agent: "Update this page without overwriting a newer revision;
use get_page and put_page, then check the durable receipt." Or: "Inspect my
writer owners with gbrain sources writer status --probe --json before changing
the setup."
Read, edit, and retry
Read an existing page with get_page and include_content: true. Preserve its
complete content, revision, and source. Send the edited complete content to
put_page with expected_revision equal to that revision and a newly generated
request_id UUID. Capture replacements, delete, restore, and version revert also
accept the revision precondition. force: true is an explicit overwrite choice;
it is mutually exclusive with expected_revision.
An omitted replacement precondition means create-only. Even byte-identical input must pass the precondition first: an old reader cannot turn a stale write into a successful no-op. A canonical content/tag/timeline/deletion/withdrawal change advances the opaque logical revision. Embeddings, summaries, and other derived rebuilds do not. Reverting an older version creates a new revision; it does not reinstate an old revision token. Legacy partial versions preserve fields that the old version did not record, including whether the page was deleted. New versions record deletion state: reverting a tombstone version removes the canonical file and hides the page; reverting a live version restores them.
Facts, takes, and canonical timeline rows commit with their page. The legacy
auto_timeline switch does not suppress that required projection; maintenance
may report zero newly reconciled rows because publication already installed them.
get_page and fetch assemble canonical page fields, tags, withdrawal state,
and the reported revision from one committed database snapshot. While a
publication is in progress, a reader may see the prior committed snapshot.
Separate calls can observe different committed revisions. Search ranking,
embeddings, and direct filesystem reads are outside this snapshot guarantee.
Do not regenerate a request ID because the response was lost or a waiter timed
out. Repeat the same operation, arguments, source, and UUID. A committed replay
returns its original result; a terminal conflict/failure is not executed again.
Changing the operation or arguments with the same UUID produces
idempotency_conflict. Resolve the conflict and use a new UUID for a new intent.
Relative times, generated capture slugs, and trusted owner/resolver defaults
are frozen at admission so retries cannot drift.
Receipt states and errors
| State | Meaning |
|---|---|
queued |
Accepted; waiting for execution. |
running |
An owner is preparing or executing it. |
recovering |
Publication must be reconciled before work on that root can continue. |
committed |
The canonical mutation and its receipt committed. |
conflict |
A precondition or identity conflict prevented commitment. |
failed |
The request ended without commitment. |
cancelled |
Cancelled before publication began. |
Receipts include request_id, state, and retry_after_ms, with optional
revision, outcome, persistence status, and timestamps. Terminal receipts have
retry_after_ms: null. Private queued content, credential hashes, and recovery
bytes are never part of the receipt.
write_pending means accepted work remains outstanding. owner_unavailable
and writer_lock_unavailable do not authorize a competing owner or a fresh
request ID. queue_capacity refuses additional admission without evicting
existing requests. revision_required, revision_conflict,
idempotency_conflict, and source_changed require correcting the caller's
intent or authority. recovery_required names unresolved publication state.
Inspect the attached receipt: absence of an acknowledgment is not evidence of
absence of a write.
CLI-generated UUIDs are retained in pending/error output. If transport delivery
is ambiguous, the client reports submission_status: "unknown" and the original
UUID, without fabricating a queued receipt or opening another PGLite engine.
Legacy callers that omit a request ID and lose the entire acknowledgment cannot
recover exact replay identity from the content alone.
Local Unix listeners keep their existing socket addresses when they fit the
portable 103-byte limit. Longer addresses use a deterministic private directory
under /private/tmp on macOS or /tmp on Linux, independent of HOME and
TMPDIR. Both CLI discovery and resident servers derive it without opening the
database. The directory must belong to the current OS user with mode 0700;
clients require a socket with mode 0600. Unsafe entries are refused. Existing
credentials and hook-secret locations are unchanged. A native binding lock
serializes startup and remains held until the listener has actually closed.
Admission retries confirmed database lock/serialization aborts for up to five seconds using the same UUID. Persistent contention returns a storage error with that UUID and no fabricated queued receipt. Keep the ID for the next attempt.
Frozen memory verbs
remember and forget accept optional request_id. Their frozen success enums
and protocol_version: 1 are unchanged. Accepted pending memory writes use the
existing unavailable error with a populated suggestion and additive
write_request/write_error metadata. A pending response never claims
status: "inserted" or expired: true.
The seven-verb surface supports recovery by repeating the original verb with
the same UUID; it does not require an unavailable status helper. A committed
forget withdraws the source- and visibility-scoped fact from active memory
even when its physical mirror is pending. Imports and rebuilds honor the
withdrawal ledger. History and backups can remain; withdrawal is not a promise
of physical erasure. See MEMORY_VERBS v1.
Git, embeddings, and physical withdrawal mirrors report their own effects
states on receipt reads. Their retries never change the committed canonical
result. Mirror recovery checks the recorded bytes and blocks its worktree if
an unexpected edit needs repair. Other worktrees can continue. Recovery space
is reserved before touching a file; insufficient capacity leaves the withdrawal
effective and its physical mirror queued.
Git work runs only for repositories already opted into durability hardening. It commits the affected file without invoking legacy hooks, then attempts a plain push to the configured tracking remote. It never pulls or rebases source files. An unconfigured remote is reported as a skipped push. Embeddings wait for an enabled, configured provider and install only if the page revision and its text projection still match.
Before managed activation, eligible put_page and capture writes also
record durable facts-extraction intent. facts_backstop.queued means that
intent committed with the page; the facts-backstop effect becomes
dispatched when its durable worker job is accepted. Extraction availability
is checked by that worker. The handoff is idempotent and rechecks the source,
page revision and current writer grant. Confined writers, unchanged pages,
disabled extraction and dream-generated content do not enqueue work.
After activation the legacy extractor reports writer_coordinator_required
and skips; it cannot bypass canonical publication. Activation also causes
previously queued extraction jobs to skip. Canonical receipts remain unchanged.
Receipt access and explicit grant migration
get_write_request, list_write_requests, and cancel_write_request require
write scope and permission for the exact helper in the current operation grant.
They are on the starter/full surfaces; they do not expand the frozen verb
surface or an agent-only tool grant. Read-only callers cannot use them.
Receipts are principal-owned. Another principal's UUID, an unknown UUID, and a
request whose target is no longer accessible return the same not_found result.
Listing selects one source and applies current operation/source/slug fences
before pagination. It exposes neither another principal's queue nor private
input. Cancellation rechecks authority under transaction locks; publication
already in progress or committed cannot be undone by cancellation.
An upgrade does not widen an existing allowedOperations snapshot. A new
profile may include helpers that an older saved profile did not. Regrant them
explicitly only when the caller needs status access; same-verb replay remains
available under the original mutation grant.
For example, suppose the reviewed existing operation list is exactly
remember,forget. A trusted administrator can preview this complete replacement
list on the brain host:
gbrain auth rescope-client client-example \
--allowed-operations remember,forget,get_write_request,list_write_requests,cancel_write_request \
--dry-run --json
Inspect before.allowedOperations, the source/slug/scope restrictions, and
before.revision. Preserve every existing operation that should remain granted.
Then apply the reviewed full list with --if-version set to that observed grant
revision; for example, if it was 7:
gbrain auth rescope-client client-example \
--allowed-operations remember,forget,get_write_request,list_write_requests,cancel_write_request \
--if-version 7 --json
The operation flag replaces the complete list. It is not an append flag. Omitted
source, slug, delegated-tool, budget, and surface flags preserve their axes.
If the client is pinned to verbs, exposing status helpers also requires an
explicitly reviewed starter/full surface within the server ceiling. New OAuth
scopes require a newly issued access token; existing tokens cannot gain scopes
by changing the client row. For resident PGLite, use the owner's authenticated
grant administration UI/API or stop the resident before ordinary
auth rescope-client; the local-writer commands below have their own resident
proxy.
Accepted requests retain their original authority snapshot and intersect it with the current grant before publication and replay. Revocation, source archive/recreation, or narrower slug/operation/holder permissions cannot be bypassed with an old receipt or queued request. Regranting receipt helpers does not rewrite an accepted mutation's authority snapshot.
Local registrations and canonical ownership
For a coordinated upgrade, update and stop older writers on every host first. Claim each filesystem source on its canonical host, then inspect writer status and existing locks. Activation is explicit:
gbrain sources writer status --probe --json
gbrain sources writer activate --confirm-quiesced --dry-run --json
gbrain sources writer activate --confirm-quiesced --json
The flag asserts that older binaries, external editors and maintenance writers
have been quiesced on every host. Activation verifies all owner bindings and
native locking, rejects outstanding legacy leases and unfinished publications,
and makes local refusal records durable before enabling managed writes. Even an
expired lease needs explicit inspection and removal; elapsed time does not prove
its writer stopped. A failed activation leaves managed mode disabled. Status
reports enabled: false until activation commits. Run an ordinary write and
read its receipt and revision before resuming writers on the upgraded hosts.
CLI and stdio registrations are durable, separate principals. The CLI lane is trusted local administration; stdio remains an untrusted memory caller. Revocation survives restart. Losing a credential file or receiving a denied response does not silently create a replacement principal.
These commands work through a credential-verified private socket when a local PGLite owner is running:
gbrain auth local-writer list --json
gbrain auth local-writer register stdio --source-ids default \
--allowed-operations remember,forget --scopes read,write --dry-run --json
gbrain auth local-writer revoke 11111111-1111-4111-8111-111111111111 --json
gbrain sources writer status --probe --json
gbrain sources writer claim default --path /absolute/canonical/source --json
register --replace requires the complete intended grant, revokes the prior
registration, and publishes a new private credential only after database
registration is durable. Output never contains the credential. A revoked CLI
cannot replace itself through the resident socket: stop the owner and explicitly
register the replacement locally. Old private files are retained for recovery,
and their revoked credentials no longer authorize work.
PGLite has one process owner. Postgres permits multiple authenticated ingress processes, but each canonical filesystem root has one designated host owner. Nested sources in a shared worktree share its coordination lock. A stale heartbeat is diagnostic information; it never authorizes taking ownership. Filesystem-dependent work waits for its owner while database reads continue.
To move a root, prepare on its current owner and retain the returned epoch and manifest digest. Copy the complete canonical worktree to the successor, then accept there with the exact epoch and digest:
gbrain sources writer transfer prepare default --json
gbrain sources writer transfer accept default --path /absolute/successor/root \
--expected-epoch 1 --manifest '<prepared-sha256>' --json
Successful preparation places the root in its draining state and records an exact path/content manifest. Changed bytes, missing files, a changed epoch, or unresolved recovery refuse acceptance. After a lost administration acknowledgment, inspect writer status and local registrations before repeating a command; administration is not automatically replayed as a page mutation.
Writer status reports resident ingress state, active preparations, owner epochs, queued request counts/bytes/age, the last committed sequence for each worktree, and recovery storage including withdrawal mirrors. Capacity entries show the configured limit, remaining reservation and the exact configuration key to adjust; usage at or above 80% includes expansion guidance. Blocked requests carry a concrete next action. Diagnostics contain no request content, credentials or private checkout paths.
Source lifecycle
After activation, source add, archive, restore, remove, purge, path rebind and
managed reclone run through the same registered owner. They take the affected
native locks, wait for publication and withdrawal mirrors to settle, then
advance every membership in a shared root. Already accepted requests for its
old topology finish with source_changed; their IDs remain permanently reserved.
Removing and recreating a source gives it a new incarnation. Source removal
retains local storage and never deletes old receipt identities.
Lifecycle commands accept --request-id for exact replay after a lost
acknowledgment and --expected-incarnation to reject a recreated source. These
UUIDs share the CLI principal's page-write ID domain: reuse for a different
operation conflicts. A lifecycle receipt can be committed, recovering, or
failed. Keep its UUID when inspecting or retrying that exact intent. A new
attempt after a terminal failure requires a new explicit UUID. --dry-run
changes neither topology nor storage.
A path rebind requires identical canonical content and deletions in a fresh candidate checkout; exclude GBrain ownership metadata when copying a candidate. A managed reclone reserves the configured recovery capacity before cloning and checks the full staged manifest before replacing the directory. If the old checkout is missing, its last verified manifest must still match the logical source; a stale remote is never accepted as recovery. Incomplete directory replacement blocks that root until its recorded recovery finishes. Neither recovery nor lifecycle administration reverses a committed fact withdrawal.
Physical checkout identity lives in private durable markers in and beside the root. Copies, replaced directories, and competing homes cannot claim that same path as separate worktrees. Keep those markers: removing them does not grant ownership or authorize failover. Old paths retain their refusal records after rebind or removal.
Bounded admission and retention
The CLI routes source mutations through the current resident owner before
opening PGLite. These administrative requests require managed activation;
before activation, stop the resident owner to use legacy source commands.
sources purge in managed mode requires an explicit archived source ID and
--confirm-destructive; use sources archived to inspect candidates. The
automatic expiry walker still coordinates each expired source separately.
--yes alone does not authorize destructive managed removal. Keep the UUID
from a pending or uncertain administrative result and repeat the same command,
arguments, and --request-id after recovery.
Default admission limits are enforced atomically:
| Reservation | Per principal | Per brain |
|---|---|---|
| Outstanding requests | 100 | 1,000 |
| Queued intent bytes | 32 MiB | 256 MiB |
| Lifetime request IDs | 100,000 | 1,000,000 |
| Terminal receipt reservation | 128 MiB | 1 GiB |
| Recovery bytes | — | 1 GiB, also 256 MiB per worktree |
Completion space is reserved at admission. Beforeimage/recovery bytes are reserved before filesystem publication. Reaching a limit refuses additional work; it does not discard an accepted request to make room. Terminal diagnostic compaction has a default eligibility threshold of 30 days and preserves replay IDs, digests, terminal outcomes, and frozen memory-verb result fields. Pending/recovering requests are not evicted. Lifetime IDs and replay protection are not silently reset.