AstralPlane is Astral's independent embedded durable-state library. It owns PostgreSQL connection and transaction mechanics, guarded schema evolution, owner-scoped repositories, audit-chain storage, transactional outbox delivery, and explicit blob-purge recovery. AstralDeep installs the package in-process; AstralPlane does not add a service port or a second database.
- Python: 3.11 or newer
- Package:
astralplane - Contract:
astralplane.contract/v1 - Current schema:
089.001, read-compatible from066.001, with guarded upgrade entry points at066.001,067.001,074.001,074.002,074.003,074.004,075.001,079.001,088.001,088.002,088.003,088.004,088.005,088.006,088.007, and088.008 - Migration advisory lock:
(1095980114, 60001) - Reconciliation advisory lock:
(1095980114, 60002)
create_postgres_runtime(...) owns psycopg2 driver-pool construction, bounded checkout, runtime
composition, and guarded startup. On a truly empty application schema it first installs the
schema-only 066.001 compatibility baseline under the migration advisory lock, then applies every
required edge of
066.001 -> 067.001 -> 074.001 -> 074.002 -> 074.003 -> 074.004 -> 075.001 -> 079.001 -> 088.001 -> 088.002 -> 088.003 -> 088.004 -> 088.005 -> 088.006 -> 088.007 -> 088.008 -> 089.001
in one registry transaction. A pre-split 066.001 database has only its legacy revision marker.
Every later predecessor, from 067.001 through 088.008, is accepted only when it carries its own
pinned historical migration-registry digest. Every supported predecessor is structurally attested
before the first migration write. A current 089.001 database must carry the exact current
registry digest and pass canonical catalog-structure verification over all Plane-owned tables,
sequences, functions, indexes, constraints, triggers, rules, policies, inheritance, and
owned-schema privileges. A same-name or unexpected object with changed behavior is rejected. A
non-empty partial or unrecognized schema is rejected rather than labeled current. The extracted
baseline contains only neutral schema and deterministic database mechanics; catalog cleanup, UI
seed content, filesystem discovery, and product policy remain explicit host reconciliation.
A fresh TEMPLATE template0 database's exact PostgreSQL default public schema
(pg_database_owner, PUBLIC USAGE) is a qualified predecessor variant. Revision 074.004
atomically transfers the selected schema to the migration user and revokes PUBLIC schema
privileges; any other predecessor owner or ACL shape remains a fail-closed mismatch.
Revision 079.001 adds persistent assignments with durable owner controls, source deduplication,
bounded task graphs, resource reservations, execution permits and immutable approval/effect
records. See persistent assignment contracts,
result publication contracts and
migration and recovery.
The package contains no AstralDeep, AstralProjection, AstralPrimitives, LETS, API, UI, agent, media, or transport implementation dependency. Product policy and authorization remain in AstralDeep; callers pass neutral owner context and retain transaction ownership.
create_repository_catalog() returns the stable repository catalog. Its 40 members, in
RepositoryCatalog.as_mapping() order, are assignments, agent_management, agents,
artifacts, attachment_parsers, audit, audit_retention, authority, background_tasks,
chat_steps, conversation_files, credentials, draft_agents,
generated_agent_publications, encrypted_llm_config, encrypted_typesafe_credential,
framework_credentials, history, harness_cleanup, identity, knowledge, maintenance,
offline_grants, outbox, preferences, personalization_graph, purge, quality_audit,
remote, remote_operation_proposals, revocations, saved_components, scheduler,
share_grants, tool_policy_state, tracked_jobs, tutorials, voice, work_admission, and
workspaces.
The stable repository catalog includes four explicit stores for the first identity/agent cutover slice:
identity: detached Keycloak/OIDC subject observations; authentication and role policy remain in Deep.agents: first-party ownership/trust plus user-agent revisions, host sessions, runtime generations, and request fences.draft_agents: owner-scoped authoring, generation leases, transition idempotency, and immutable publication records.tool_policy_state: explicit scope rows, legacy and per-kind tool overrides, saved selections, and per-user agent opt-outs; permission decisions remain in Deep.
Use the matching create_identity_repository(), create_agent_repository(),
create_draft_agent_repository(), and create_tool_policy_state_repository() factories when a
composition does not need the full catalog. See docs/identity-agent-state.md for transaction and
owner-isolation rules.
agents.reconcile_validation_policy_for_administration(...) is the atomic, advisory-locked
startup surface for a Deep-supplied opaque product-policy revision. Exact marker replay is
write-free; a changed marker flags only live mismatched agents in the same caller transaction.
The next schema-neutral catalog slice exposes ciphertext and grant mechanics already present in
the 066.001 baseline:
credentials: opaque user-agent credentials plus owner-bound remote-machine credentials, with explicit compare-and-set replacement and a bounded administrative re-encryption page.offline_grants: encrypted refresh-token records, token-free standing-grant lookup, and owner-scoped idempotent revocation.share_grants: immutable snapshot capabilities stored by digest, metadata-only owner listing, and an active-state-checked public open counter.
Use create_credential_repository(), create_offline_grant_repository(), and
create_share_grant_repository() for individual composition. Encryption, raw token handling,
Keycloak exchange, PHI policy, rendering, and audit decisions remain in AstralDeep. See
docs/credentials-and-grants.md for the owner, replay, and transaction contracts.
Conversation-adjacent durable state has three additional stable factories:
create_chat_step_repository()for owner/turn-checked progress trails and terminal-state CAS;create_conversation_file_repository()for ordered opaque file-link metadata; andcreate_saved_component_repository()for the same publication-aware component implementation already used by Plane workspaces.
They are cataloged as chat_steps, conversation_files, and saved_components. Step redaction and
delivery, uploads, parsing, blob I/O, and canvas policy remain product-owned. See
docs/conversation-extended-state.md.
Attachment parser persistence and physical blob mechanics now have explicit composition surfaces:
create_attachment_parser_repository()is cataloged asattachment_parsers. It exposes redacted global coverage separately from owner-scoped claim provenance, atomically deduplicates pending/live gaps, reclaims only failed/discarded gaps, and fences lifecycle changes by status plusupdated_at.create_streaming_blob_store(root=...)adds pathless bounded readers, a narrowly scoped read-only parser lease, cross-process owner exclusion, and hidden staging reservations. It securely provisions only the configured root's missing suffix below the nearest existing, link-free absolute ancestor. Direct publication and deletion are deliberately absent.create_attachment_materialization_coordinator(...)is the only production creation composite: it commits a pending metadata intent, opens an unpublished staging session under the exact owner/lease row fence, and publishes bytes plus READY metadata in one short transaction.create_durable_purge_executor(...)consumes typed attachment-prefix/owner-namespace tombstones, performs capability-bound physical deletion on that same store, verifies absence, and records a version-fenced terminal result.
Parser generation/execution and administrator authorization remain in AstralDeep. See
docs/attachment-parser-and-blob-composition.md for ownership, retry, purge, and recovery rules.
The remaining knowledge, personalization-graph, and scheduler-extended baseline state is exposed
through create_knowledge_repository(), create_personalization_graph_repository(),
create_background_task_repository(), create_maintenance_repository(), and
create_tracked_job_repository(). create_scheduler_repository() owns scheduled definitions,
occurrences, runs, effects, and atomic chat publication while WorkAdmission remains separate. The
full catalog keys include knowledge, personalization_graph, background_tasks, maintenance,
scheduler, and tracked_jobs. Owner-scoped reads,
immutable replay identities, status/timestamp/lease-generation compare-and-set transitions, and
explicitly named administrative surfaces prevent a caller from accidentally treating global work
as ordinary user state.
AsyncPlaneRuntime is a bounded event-loop adapter over whole caller-owned synchronous
transactions. It does not provide async raw-SQL helpers or connection access. See
docs/knowledge-scheduler-and-async-contracts.md for the exact lifecycle and cancellation rules.
The public work_admission catalog member owns durable operation admission, finite hierarchical
capacity, submission replay, execution leases, fenced terminalization, request-generation binding,
and bounded retention. configure() and load_existing_configs() return detached snapshots;
bind_configs() publishes one only after the caller-owned transaction commits. The repository
validates every public type, timestamp, duration, code, terminal payload, and limit before SQL.
Revision 074.004 retains the owner-partitioned qualification-audit and bounded host-session
compatibility introduced through 074.003, and adds durable pending attachment materialization,
typed purge scope, retired-owner admission fencing, canonical owner/attachment case-fold isolation,
expired-upload recovery, and whole-schema catalog verification. quality_audit provides run, case,
evidence, audit-entry, and LaTeX-artifact
records. Review plus case-status transition is one caller-owned atomic operation with a locked
owner chain head and a versioned full-record hash; legacy v1 entries remain readable without being
silently rewritten. Tutorial content/revisions, remote-operation proposals, feedback paging and
deduplication, personalization mutation, external-identity linking, and the other extended-state
facades are likewise available only through named typed catalog members.
Repository writes using the shared canonical JSON encoder require string keys in every mapping,
including mappings nested inside lists and tuples. Non-string keys raise RepositoryValidationError before SQL rather
than being converted to strings and colliding with existing string keys. Callers that supplied
non-string keys must update their inputs. Valid canonical JSON bytes remain unchanged; this
validation does not rewrite existing stored payloads, digests, or schema metadata.
Revision 075.001 adds the immutable voice_session.speech_backend discriminator. Historical
sessions backfill to llm_factory; new client_local rows carry no remote room, participant,
worker, or media-grant metadata. Voice-turn persistence remains unchanged, and Plane adds no audio,
transcript, local-engine, proof, or client-capability storage.
The revisions after 079.001:
088.001adds one-shot operation admission for persistent assignments, with an execution-profile discriminator and original-key operation receipts; see persistent assignment contracts.088.002adds a database-issuedweb_sessionincarnation identity, and088.003adds nullable issuing issuer/client metadata to sessions and an issuing issuer to deferred revocations; seedocs/identity-agent-state.md.088.004stores declarative agent definitions as immutable revisions with metadata-only receipts.088.005adds owner guidance storage; see owner guidance contracts.088.006adds immutable selected-input envelopes; see selected input contracts.088.007adds optional scheduled-job policy and occurrence-assignment bindings; seedocs/knowledge-scheduler-and-async-contracts.md.088.008adds hash-only framework credentials and finite offline-grant allowances; seedocs/credentials-and-grants.md.089.001adds owner-keyed TypeSafe credential ciphertext and third-party data-sharing acknowledgments.
Upgrade and recovery procedures for every revision are in migration and recovery.
AstralPlane owns qualification of its Python source, architecture boundary, PostgreSQL migration
and repository behavior, and standalone package compatibility. Pull requests and main pushes run
the repository-owned .github/workflows/ci.yml jobs quality, postgresql, and
package-compatibility; the gates aggregate fails closed unless every owner job succeeds. The
PostgreSQL lane runs the complete Python 3.11 suite against PostgreSQL 17 with a measured-baseline
combined branch-coverage floor of 88.75% and a changed-line coverage threshold of 90%.
scripts/check_changed_coverage.py decides changed-line coverage from diff-cover's JSON report for
the committed range BASE_SHA..HEAD, where BASE_SHA is the pull request's base commit or, on a
push to main, the commit before the push. It fails below 90%; a change with no measurable
executable lines is recorded in the step summary as not applicable, naming the base and candidate
SHAs and every changed path considered. A missing, malformed, or all-zero BASE_SHA, a base equal
to the candidate, or a report that does not describe that range fails closed. Package
compatibility builds and installs a clean wheel on Python 3.11 and 3.14; it does not replace the
PostgreSQL production lane.
uv lock --check
uv sync --frozen --group ci
uv run --frozen --group ci ruff check .
uv run --frozen --group ci python tests/architecture/test_dependency_direction.py
ASTRALDEEP_SOURCE_REPO=/path/to/AstralDeep \
ASTRALPLANE_TEST_POSTGRES_DSN=postgresql://user:password@127.0.0.1:5432/isolated_database \
uv run --frozen --group ci pytest -q -p no:cacheprovider \
--cov=astralplane --cov=scripts.check_changed_coverage \
--cov=scripts.import_staging_fixture --cov=scripts.migrate_qualification_database \
--cov-branch --cov-report=xml --cov-fail-under=88.75
BASE_SHA="$(git merge-base origin/main HEAD)"
uv run --frozen --group ci diff-cover coverage.xml --compare-branch "$BASE_SHA" \
--diff-range-notation '..' --ignore-staged --ignore-unstaged \
--format json:changed-coverage.json
uv run --frozen --group ci python scripts/check_changed_coverage.py \
--report changed-coverage.json --base-sha "$BASE_SHA" --fail-under 90
uv lock --check
uv build --build-constraints tooling/python-ci/build-requirements.lock.txt --require-hashes
actionlint .github/workflows/ci.yml
PostgreSQL integration checks use an isolated test database and the synthetic non-PHI fixture under
tests/fixtures/pre_split. Set ASTRALPLANE_TEST_POSTGRES_DSN to that isolated database, whose
role must be able to create and drop databases because migrated templates are cloned per test, and
run both tests/integration/test_pre_split_upgrade.py and
tests/integration/test_empty_database_startup.py. An unset URL reports the checks as skipped, not
passed. Runtime databases, blobs, uploads, logs, credentials, generated content, and local
environments must never be committed or placed beneath the package/submodule tree.
AuditOutboxDelivery retries a failed sink with exponential delays, saturating at
max_retry_delay before multiplying a timedelta. The exponent remains capped at 30;
defaults remain eight attempts, a five-second base delay, and a one-hour maximum delay.
Microsecond precision is preserved, including when the cap is not an exact multiple of
the base delay.
If the capped delay would place the next retry after UTC datetime.max, delivery raises
PlaneError with code audit_retry_out_of_range and the entry ID. It does not change the
claimed outbox row or report a successful settlement; the existing lease can be reclaimed
after expiry. A retry exactly at datetime.max is representable and remains valid.
An exhausted attempt dead-letters without calculating another retry time. Retry and
dead-letter outcomes are reported only after their fenced state transition commits.
See docs/migration-and-recovery.md before changing schema or durable roots.