Consumed Contract Ledger¶
This ledger records external contracts that aws-tui consumes and the pinned versions checked during maintenance. It is not a replacement for tests; it is a durable map of the real upstream surfaces that mocks and adapters must track.
1. 2026-07-01 maintenance pass¶
| Integration point | Pinned version / ref | Consumed contract | Verification method |
|---|---|---|---|
aioboto3.Session().client("s3", ...) via S3FS and S3Mock seed scripts |
aioboto3==15.5.0, aiobotocore==2.25.1 from uv.lock |
Async S3 client creation accepts botocore Config, endpoint_url, and verify. Production consumes ListBuckets, ListObjectsV2, HeadObject, GetObject, PutObject, CopyObject, DeleteObject, DeleteObjects, multipart upload, and abort operations. |
Source trace through src/aws_tui/domain/s3_fs.py, src/aws_tui/services/s3/service.py, and scripts/test-services/s3/seed.py; focused unit coverage includes partial batch-delete failures and TLS propagation; the Docker-backed tier exercises nine flows against an independent S3-compatible implementation. |
| Boto credential/profile and S3-compatible options | boto3==1.40.61, botocore==1.40.61 from uv.lock |
Production creates clients through aioboto3 and configures botocore profiles, regions, endpoint URLs, path-style addressing, TLS verification, six total adaptive attempts, 10-second connect timeouts, 60-second read timeouts, and temporary session tokens. Startup follows botocore's AWS_DEFAULT_PROFILE then AWS_PROFILE precedence and uses AWS_DEFAULT_REGION after connection/profile regions. AWS_CONFIG_FILE and AWS_SHARED_CREDENTIALS_FILE override expanded shared-file paths. Direct boto3 consumption is transitive/test tooling rather than a production import. |
Source trace through src/aws_tui/infra/aws_session.py, src/aws_tui/infra/connection_resolver.py, and S3 provider construction; config parsing rejects invalid string and boolean shapes before values reach botocore. Environment precedence and path overrides have integration coverage. |
| EMR Serverless client and log-discovery contracts | botocore==1.40.61 service model, EMR Serverless API 2021-07-13, and AWS EMR Serverless logging documentation |
Operations ListApplications, ListJobRuns, GetJobRun, and StartJobRun; consumed request fields applicationId, jobRunId, list-valued states (up to eight values), maxResults=50, and nextToken; response fields applications, jobRuns, jobRun, and pagination nextToken; ApplicationState and JobRunState enums. S3 log discovery accepts retry prefixes (attempts/<n>), Spark driver and SPARK_EXECUTOR/<id> paths, Hive HIVE_DRIVER and TEZ_TASK/<id> paths, and active or rotated stdout/stderr gzip objects. Discovery fails closed above 100 S3 pages or 200 classified files before the list becomes VM and widget state. The application supplies a stable per-intent clientToken for StartJobRun (see the idempotency entry below). |
Installed botocore model introspection, AWS's public S3 logging layout, and source trace through src/aws_tui/domain/emr_serverless.py, src/aws_tui/domain/emr_logs.py, and src/aws_tui/services/emr_serverless/service.py; request-shape and log-layout tests cover mapping, pagination and collection ceilings, retries, worker identity, rotation, detail/log panes, clone-from-run, and bounded streaming. |
| AWS Glue read contracts | botocore==1.40.61 service model, Glue API 2017-03-31 |
Operations GetDatabases, GetTables, GetTable, GetPartitions, GetColumnStatisticsForTable, GetJobs, GetJobRuns, GetCrawlers, GetCrawler, GetCrawlerMetrics, and GetTags, plus STS GetCallerIdentity for crawler tag ARNs; Glue pagination tokens and optional response fields are normalized into immutable domain values. |
Installed botocore model introspection against the locked environment; source trace through src/aws_tui/domain/glue.py and src/aws_tui/services/glue/service.py; mapper, paginator, error, VM, integration, demo, and snapshot tests cover the shipped read-only views. |
| Single-context AWS service source identity | Internal ServiceSourceContext / RootVM contract |
EMR, Glue, and Athena use a bordered selector for the active AWS connection's name, optional distinct profile, and region; remembered selections are scoped by service id, connection name, and region. Shift+S rebuilds the active service under the next supported AWS connection, while Shift+A cycles the EMR application. Service-level AccessDenied is not a connection-reachability signal. OpenS3LocationRequest carries the exact connection name and region; the app rejects missing identities or region drift instead of substituting another profile. |
tests/unit/vm/test_service_source_vm.py, message tests, EMR/Glue/Athena integration coverage, source-header snapshots across shipped themes, and E2E source-switch/S3-handoff journeys verify identity rendering, scope isolation, key routing, remount behavior, and cross-service identity preservation. |
| Textual app/runtime API | textual==8.2.8 from uv.lock and the exact runtime requirement |
App launch, bindings, modal/screen stack, widgets, pilot tests, and snapshot rendering. Compatibility code is isolated to Textual 8.2.8 behavior: priority binding replacement touches _bindings.key_to_bindings, app crash recovery overrides _handle_exception, and the content-host mount boundary interprets lifecycle tracebacks rooted in _pre_process. |
Full integration/snapshot tests exercise startup, priority bindings, lifecycle recovery, modal controls, focus cycling, settings, themes, and demo mode. The installed 8.2.8 source was inspected; supporting another Textual release requires an explicit adapter re-audit before broadening the pin. |
| VMx view-model helpers | vmx==3.1.0 from uv.lock |
VM lifecycle, observable state, message protocol, command disposal, and form/composite/dialog/pagination helper contracts referenced by the VM layer and docs. | Import-level compatibility smoke plus focused VM tests against the locked environment; this branch also records the VMx 3.1.0 adoption audit and keeps larger adapter replacements deferred behind that report. |
| SQL parsing and Iceberg identifier extraction | sqlglot==30.14.0 from uv.lock |
Public parse and parse_one entry points with the Athena dialect, AST traversal, statement classification, and quoted table-identifier extraction. |
Source trace through src/aws_tui/domain/sql_policy.py and src/aws_tui/domain/iceberg.py; SQL-policy and Iceberg suites cover accepted read-only forms, rejected mutations, complexity bounds, and identifier quoting. |
| Config, path, and secret-storage helpers | keyring==25.7.0, tomli-w==1.2.0, platformdirs==4.11.0 from uv.lock |
OS keychain get/set/delete behavior, TOML serialization for config.toml, and platform-native config/cache path resolution with legacy fallback directories. Production Settings saves store only keychain:<service> references in TOML; key material stays in the OS-backed keyring. Atomic edits alternate between two bounded revision slots and publish the newly written service reference before deleting the superseded secrets. Legacy static entries remain readable and migrate when edited; no first-run credential form is currently shipped. |
Source trace through src/aws_tui/infra/keychain.py, src/aws_tui/infra/config_store.py, src/aws_tui/infra/paths.py, and src/aws_tui/vm/settings/s3_connections_vm.py; unit coverage exercises keyring CRUD, rollback, resolver integration, strict TOML parsing, private permissions, and platformdirs behavior. |
| Python package build backend | hatchling==1.31.0 from uv.lock; build-system.requires constrained to hatchling>=1.31.0,<2 |
PEP 517 wheel/sdist build behavior, package metadata, and version-file inclusion. | Build backend installed in the locked dev environment; CI and release run uv build --no-build-isolation so artifacts do not resolve an untracked hatchling version at build time. |
| GitHub Actions CI/release/publish workflow | Workflow refs at the 2026-07-01 pass: actions/checkout v4, astral-sh/setup-uv v8.3.2, actions/upload-artifact v7, actions/download-artifact v7, pypa/gh-action-pypi-publish v1, peter-evans/create-pull-request v6, actions/configure-pages v5, actions/upload-pages-artifact v3, and actions/deploy-pages v5.0.0; uv==0.11.19 |
Checkout, pinned uv installation, CI/build artifact upload, release artifact download, Sigstore/OIDC PyPI publishing, TestPyPI rehearsal, GitHub Release asset upload, Homebrew tap PR creation, and Pages build/deploy. | Historical workflow source review; superseded current pins are recorded in §4. |
| Pre-commit hooks | Immutable refs resolved on 2026-08-02: pre-commit-hooks@3e8a8703264a2f4a69428a0aa4dcb512790b2c8c (v6.0.0), ruff-pre-commit@39d9ac5938dadb73df0564a45f163e25ff9fa6e2 (v0.16.1), taplo-pre-commit@ade0f95ddcf661c697d4670d2cfcbe95d0048a0a (v0.9.3 peeled commit); local mypy via locked env |
Formatting, linting, type checking, TOML validation, trailing whitespace, EOF, and large-file hygiene. | git ls-remote verification of each tag before pinning; local equivalent checks run through the locked uv 0.11.19 environment, and CI uses setup-uv v9.0.0 with uv sync --frozen. |
2. 2026-07-26 Athena service pass¶
| Integration point | Pinned version / ref | Consumed contract | Verification method |
|---|---|---|---|
| Amazon Athena client | botocore==1.40.61 service model, Athena API 2017-05-18 |
The AthenaClient uses exactly the 16 boto operation methods listed below, including batched history hydration and prepared-query detail. It always supplies WorkGroup, QueryExecutionContext (catalog/database), and a client request token to start_query_execution. AthenaClient.start_query(...) accepts an optional output_location and adds ResultConfiguration.OutputLocation only when its caller supplies that value. The shipped AthenaQueryVM query runner does not supply output_location; it relies on the selected workgroup's enforced customer S3 or Athena managed-results configuration. List/result continuations are opaque NextToken values. |
Source-derived operation inventory is compared exactly with the locked Botocore model, with explicit high-risk input-member checks. Unit tests cover every operation, both start-query request shapes, response mapping, pagination, output-configuration failures, error normalization, and result-header handling. |
| Athena page and result handoff | Internal AthenaPageVM / OpenS3LocationRequest contract |
Query, History, Results, and Saved are connection- and region-scoped. Athena renders Source, Workgroup, Catalog, and Database as four individually framed selectors in an unframed row; its focus order is source, workgroup, catalog, database, tabs, then the active view. Query controls precede the Query editor and Execution detail; Run and Stop use stable 5x3 terminal geometry with positive-height content fully inside the controls frame. The client accepts one read-only parsed statement, can interrupt submission and stop only app-started active executions, and loads results a page at a time with a 10,000-row cumulative VM ceiling. A Glue handoff prefills a database/table-qualified SELECT * starter ending in LIMIT 5, supplies the exact catalog/database through QueryExecutionContext, explicitly projects SQL into the mounted editor, and never auto-executes it. Known result-location, context, and workgroup start rejections retain fixed redacted categories through the runner and VM. A result handoff reloads the selected execution and requires a succeeded execution, matching active connection/region/context, and a valid s3:// output location before handing the exact identity to S3. Full SQL, raw boto responses, and result values do not enter logs or crash dumps. |
tests/unit/domain/test_athena.py, tests/unit/domain/test_sql_policy.py, Athena VM tests, tests/integration/test_athena_page.py, tests/integration/test_glue_athena_navigation.py, tests/integration/test_athena_s3_handoff.py, demo tests, and Athena snapshots. |
Exact boto operation ledger (16):
list_work_groups
get_work_group
list_data_catalogs
list_databases
list_table_metadata
list_query_executions
get_query_execution
batch_get_query_execution
get_query_runtime_statistics
start_query_execution
stop_query_execution
get_query_results
list_named_queries
batch_get_named_query
list_prepared_statements
get_prepared_statement
3. 2026-07-28 Glue, Athena, and Iceberg integration pass¶
| Integration point | Pinned version / ref | Consumed contract | Verification method |
|---|---|---|---|
| AWS Glue client | botocore==1.40.61 service model, Glue API 2017-03-31 |
GlueClient uses exactly the 11 Glue boto operations below. get_caller_identity is the sole STS call and supplies the account ID used to construct a crawler ARN before get_tags. All pages preserve opaque continuation tokens and map raw SDK responses into immutable domain records. |
tests/unit/domain/test_glue.py, Glue VM/service/integration tests, and documentation source comparison cover operations, mapping, pagination, tags, errors, and exact record shapes. |
| Shared catalog/query records | Internal domain.data_catalog and domain.query contracts |
TableRef(catalog_name, database_name, table_name, connection_name, region) is the complete cross-service table identity. QueryContext(connection_name, region, workgroup, catalog, database) is the Athena execution identity. TableFormat classifies Iceberg, Hive, Hudi, Delta, and Other from normalized Glue metadata. |
Domain tests cover exact format markers, malformed values, quoting, scope equality, and foreign-context rejection. Navigation integration tests exercise exact and missing connection/region cases. |
| Iceberg metadata inspection | Internal IcebergInspector over AthenaQueryRunner; Apache Iceberg metadata tables exposed by Athena |
IcebergInspector reads snapshots, history, manifests, files, partitions, and refs with hard row limits of 100/100/500/1000/500/100. The five fixed-schema views use explicit projections and ORDER BY; partitions intentionally uses SELECT * with LIMIT 500 without ORDER BY because its metadata schema is dynamic. It uses the same public Athena operations already listed in §2; no private SDK API or extra boto operation is introduced. GlueService revalidates a remembered enabled workgroup in the same connection/region before constructing the QueryContext. |
Iceberg domain, runner, Glue service/VM/UI, integration, demo, snapshot, and E2E tests cover shape validation, pagination, partial failure, retry, cancellation, profile isolation, time travel, and result artifacts. |
| Cross-service messages | Internal immutable VM message contract | OpenAthenaTableRequest(TableRef, snapshot_id?), CopyTableReferenceRequest(TableRef), OpenGlueTableRequest(TableRef), and OpenS3LocationRequest(connection_name, region, uri, preferred_pane, reveal_object) are exact runtime-validated envelopes. CopyTableReferenceRequest stores the canonical identifier and source identity in the authoritative typed in-app clipboard; the OS clipboard write then goes through the single app-level writer, which reports the checkable clipboard port's result and never reports an unacknowledged OSC 52 write as a copy. The composition root serializes table handoffs, rejects missing/changed identities, and restores complete representable Athena query/result state plus Glue catalog, Iceberg view, and snapshot selection when a mutation fails or is superseded. |
Message unit tests plus Glue↔Athena and Athena/Glue→S3 integration tests cover runtime validation, typed clipboard delivery, bounded discovery, full-state rollback, cancellation, shutdown, and stale request suppression. |
| Public actions | Internal ActionRegistry, KeymapStore, and command palette contract |
The complete source-derived ledger below includes Glue/Athena view commands, selector commands, execution controls, and cross-service actions. Glue copy requires a selected Catalog table. Athena insert requires a typed clipboard value from the active Athena connection and region; a missing or foreign-source value leaves the editor and clipboard unchanged. Athena remains a single-context service, and generated SQL is a quoted database/table-relative LIMIT 5 starter without auto-execution. The active service remains selected while focus is inside its content. A fitted one-line Commands legend is centered; enabled visible chips dispatch the same action registry path as shortcuts and palette entries, disabled chips are inert, and clickability does not add Tab stops. Its overflow behavior and shortcut bindings are unchanged. |
Keymap fidelity, command-palette, action-routing, Glue/Athena navigation, nav-menu, legend, and E2E tests pin availability, selector routing, source guards, focus selection, fitted legend behavior, click parity, and no-auto-run behavior. |
| Demo providers and journeys | Internal in-memory EMR/Glue/Athena/S3 contracts | demo-dev, demo-prod, and demo-shared expose disjoint EMR applications/runs and Iceberg records plus resolvable log, metadata, data, and result S3 URIs. Returning to a connection reuses that connection's EMR client without sharing mutable state across profiles. In-memory Athena validates exact contexts, request token bounds, canonical S3 output locations, opaque operation/context-bound pagination tokens, collection fingerprints, and UTF-8 before mutation. Result CSV bytes match the displayed ResultPage. |
Demo provider boundary tests, three-profile content/snapshot guards, navigation tests, and Journey 9 verify Glue → Athena → explicit execution → S3 without network access. |
Exact Glue boto operation ledger (11):
get_databases
get_tables
get_table
get_partitions
get_column_statistics_for_table
get_jobs
get_job_runs
get_crawlers
get_crawler
get_crawler_metrics
get_tags
Exact STS boto operation ledger (1):
get_caller_identity
Public Glue and Athena action ledger:
athena.cancel
athena.choose_catalog
athena.choose_database
athena.choose_workgroup
athena.execute
athena.history
athena.insert_table_ref
athena.load_more
athena.open_in_glue
athena.open_result_location
athena.query
athena.results
athena.saved
glue.catalog
glue.choose_crawler_state
glue.choose_run_state
glue.copy_table_ref
glue.crawlers
glue.jobs
glue.load_more
glue.open_s3_location
glue.query_in_athena
glue.time_travel_in_athena
Public cross-service message ledger:
OpenS3LocationRequest
OpenAthenaTableRequest
CopyTableReferenceRequest
OpenGlueTableRequest
4. 2026-08-02 maintenance pass¶
| Integration point | Pinned version / ref | Consumed contract | Verification method |
|---|---|---|---|
| Python runtime dependency ownership | vmx==3.1.0, textual==8.2.8, reactivex==4.1.0, and rich==15.0.0 from uv.lock |
Every production import is directly declared: VMx provides lifecycle/composition primitives, Textual owns the TUI runtime, Reactivex owns observable protocols/subjects, and Rich provides markup escaping. boto3 is no longer declared directly because production creates clients through aioboto3; Botocore remains direct for configuration, models, and exceptions. VMx's documented deep module paths remain supported public imports; root-facade imports are preferred for newly touched code, without a mechanical 46-module churn. |
Built wheel metadata inspection, import inventory over src/aws_tui, exact installed-source inspection, isolated wheel smoke, and full unit/integration/snapshot/E2E coverage. |
| GitHub Actions CI/release/publish workflow | actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 (v7.0.1), astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 (v9.0.0), actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a (v7.0.1), actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c (v8.0.1), pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 (v1.14.2 peeled commit), peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 (v8.1.1), actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d (v6.0.0), actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 (v5.0.0), and actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 (v5.0.1) |
Current major-version contracts for checkout, uv installation/cache, artifact exchange, trusted PyPI publishing, Homebrew PR creation, and Pages deployment. CI's stable ci gate now includes an unconditional documentation-contract job. |
GitHub release API plus peeled git ls-remote tag verification on 2026-08-02; workflow guard tests, YAML parsing, local docs build, package build, and installed-wheel smoke. |
5. 2026-08-25 maintenance pass¶
| Integration point | Pinned version / ref | Consumed contract | Verification method |
|---|---|---|---|
| Python runtime and AWS SDK graph | aioboto3==15.5.0, aiobotocore==2.25.1, botocore==1.40.61, textual==8.2.8, vmx==3.23.0, reactivex==5.1.0, rich==15.0.0, keyring==25.7.0, tomli-w==1.2.0, platformdirs==4.11.5, sqlglot==30.17.0, anyio==4.14.2, and aiofiles==25.1.0 from uv.lock |
VMx and the other runtime packages use public facades or documented module paths. The Textual compatibility adapter uses exact-version private hooks (_bindings, _pre_process, and _handle_exception) because Textual 8.2.8 has no equivalent public binding-replacement and lifecycle-recovery surface; the exact Textual pin prevents unreviewed drift. VMx owns command admission/cancellation, modal focus restoration, immutable form construction, observable state, component lifecycle, filtering, and paging. AWS operations and request members are validated against the locked Botocore models. |
Full unit, integration, snapshot, and E2E tiers; minimum-direct-dependency tests; import/layer checks; VMx compatibility regressions for command cancellation, retired pager generations, form validation, and modal restoration; source-derived Botocore operation and input-member tests. |
| VMx 3.23 compatibility and specialization | vmx==3.23.0 from uv.lock; runtime requirement vmx>=3.23.0,<4.0.0 |
FocusCoordinatorVM delegates modal save/restore behavior to public DiscriminatorVM.modal_open() / modal_close(). S3ConnectionFormVM supplies complete field and model validation through FormVMBuilder at construction time. Athena drains public AsyncRelayCommand.is_executing admission state after cancellation and tracks the provider task behind nested command execution so shutdown waits for cancellation-resistant I/O. No VM reaches into VMx private fields. |
Focus, Settings form, Athena query/results, VMx smoke, mypy, and lifecycle tests run against the locked package. The dated VMx 3.23 maintenance report records adopted and rejected candidates plus production-line metrics. |
| Bounded AWS operational state | Botocore service contracts above plus internal provider-error taxonomy | EMR requests at most 50 applications or job runs per page; application discovery stops above 100 pages or 1,000 records, and bulk job-run discovery stops above 100 pages. User-driven Glue Catalog, Glue Jobs, Glue Crawlers, and EMR job-run collections stop at 1,000 items through a bounded VMx token-pager specialization. Athena history, saved-query, and context collections stop at 1,000 items, while result rows stop at 10,000, through an app-owned bounded snapshot pager that retains VMx commands. A visible, snapshot-stable safety-limit state replaces the ordinary load-more affordance when a ceiling ends pagination. Log discovery classifies only the run-relative suffix so user prefixes cannot impersonate worker markers. Recursive S3 deletion stops above 100 listing pages or 10,000 objects and reports how many objects were already removed. S3 SlowDown and RequestLimitExceeded responses map to ThrottledError; service availability and transport failures map to ProviderUnreachableError. |
Exact request-shape and pager tests assert page size, page ceilings, collection ceilings, final-page clipping, refresh and snapshot preservation, visible truncation state, partial-delete diagnostics, repeated-token rejection, run-relative log classification, and throttling versus reachability behavior. |
| Bounded local and cross-provider state | Internal InMemoryFS and CrossFs contracts |
Demo objects are limited to 100 MiB each and 256 MiB in aggregate, and a directory listing fails closed above 10,000 entries. Recursive cross-provider copy and move traversals stop above 10,000 entries or 128 levels; owned staging data is cleaned after a limit failure. | In-memory filesystem boundary tests cover known-size and streamed writes, overwrite accounting, and listing ceilings. Cross-filesystem tests cover entry/depth rejection and stage cleanup. |
| Packaging and developer tooling | hatchling==1.32.0, testcontainers==4.15.0, and textual-dev==1.8.0 from uv.lock; build-system.requires constrained to hatchling>=1.31.0,<2 |
CI, release, Pages, and bootstrap sync/export with --locked, so a stale lock fails instead of silently installing it. Bootstrap installs all dependency groups. The Textual development CLI used by scripts/dev.sh is declared explicitly. Wheel and sdist members must exclude repository metadata, tests, local caches, and traversal paths; both artifacts must contain every source Python module, py.typed, and packaged theme stylesheet, while the sdist must also retain its build metadata and PyPI readme. |
uv lock --check, script/workflow guard tests, real wheel/sdist builds, scripts.check_dist, Twine metadata checks, isolated wheel and sdist install smoke, and a Textual CLI invocation. |
| GitHub Actions and pre-commit toolchain | astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d (v10.0.1), ruff-pre-commit@1f1e8bf348ff38fc88619a38d3ca4d9c56abea49 (v0.16.5), pre-commit-hooks@3e8a8703264a2f4a69428a0aa4dcb512790b2c8c (v6.0.0), taplo-pre-commit@ade0f95ddcf661c697d4670d2cfcbe95d0048a0a (v0.9.3), and the other immutable action refs listed in §4 |
Workflow jobs retain least-privilege permissions and bounded timeouts. CI and release export every locked dependency group before pip-audit. Pages and wiki publication run only from main, including manual dispatch, so a branch-controlled checkout cannot receive the wiki deploy key. The wiki deploy key is checked before it is written. The Homebrew checkout does not persist its cross-repository token. Artifact contents are checked before upload or publication. |
Official tag/ref verification, YAML guard tests, pre-commit, shellcheck, and local workflow-equivalent package/docs commands. |
| Local S3-compatible harness | Docker image adobe/s3mock:5.2.0@sha256:7a37f0d796e81a28b970c892dcae532797014616b3312b467af8f0274ebf0c26; testcontainers==4.15.0 from uv.lock |
Independent S3-compatible endpoint, readiness probe, seeded buckets/objects, path-style config, arbitrary test credentials, and host port exposure. | The former MinIO community image was removed because every usable community release is affected by GHSA-hv4r-mvr4-25vw, while the nominally fixed AIStor image requires a commercial license and rejects writes without one. Adobe S3Mock 5.2.0 is license-free, digest-pinned, exposed only on 127.0.0.1, and passes the same nine strict S3FS/CrossFs protocol tests through the public testcontainers.core.container.DockerContainer API. Manual trace also covers scripts/test-services/s3/docker-compose.yml, seed.py, and config-snippet.toml. |
Exact S3 boto operation ledger (16):
AbortMultipartUpload
CompleteMultipartUpload
CopyObject
CreateBucket
CreateMultipartUpload
DeleteObject
DeleteObjects
GetObject
GetObjectTagging
HeadBucket
HeadObject
ListBuckets
ListObjectsV2
PutObject
UploadPart
UploadPartCopy
6. 2026-09-10 dependency maintenance pass¶
Carries §5 forward at new pins. Only the four refs below moved; every other
contract, boundary, and verification method is unchanged and restated here so
this section is the single authoritative record of what is currently in force.
anyio 4.14.2 → 4.15.1, platformdirs 4.11.5 → 4.11.8, sqlglot 30.17.0 →
30.18.0, and ruff-pre-commit v0.16.5 → v0.16.6. The three locked packages are
transitive-facing utilities consumed through public APIs; ruff-pre-commit is
lint tooling whose ref was verified against the upstream tag rather than taken
from the bump alone. Supersedes dependabot #194, #195, #196, and #197, which
could not update this ledger themselves and so could never go green.
| Integration point | Pinned version / ref | Consumed contract | Verification method |
|---|---|---|---|
| Python runtime and AWS SDK graph | aioboto3==15.5.0, aiobotocore==2.25.1, botocore==1.40.61, textual==8.2.8, vmx==3.23.0, reactivex==5.1.0, rich==15.0.0, keyring==25.7.0, tomli-w==1.2.0, platformdirs==4.11.8, sqlglot==30.18.0, anyio==4.15.1, and aiofiles==25.1.0 from uv.lock |
VMx and the other runtime packages use public facades or documented module paths. The Textual compatibility adapter uses exact-version private hooks (_bindings, _pre_process, and _handle_exception) because Textual 8.2.8 has no equivalent public binding-replacement and lifecycle-recovery surface; the exact Textual pin prevents unreviewed drift. VMx owns command admission/cancellation, modal focus restoration, immutable form construction, observable state, component lifecycle, filtering, and paging. AWS operations and request members are validated against the locked Botocore models. |
Full unit, integration, snapshot, and E2E tiers; minimum-direct-dependency tests; import/layer checks; VMx compatibility regressions for command cancellation, retired pager generations, form validation, and modal restoration; source-derived Botocore operation and input-member tests. |
| VMx 3.23 compatibility and specialization | vmx==3.23.0 from uv.lock; runtime requirement vmx>=3.23.0,<4.0.0 |
FocusCoordinatorVM delegates modal save/restore behavior to public DiscriminatorVM.modal_open() / modal_close(). S3ConnectionFormVM supplies complete field and model validation through FormVMBuilder at construction time. Athena drains public AsyncRelayCommand.is_executing admission state after cancellation and tracks the provider task behind nested command execution so shutdown waits for cancellation-resistant I/O. No VM reaches into VMx private fields. |
Focus, Settings form, Athena query/results, VMx smoke, mypy, and lifecycle tests run against the locked package. The dated VMx 3.23 maintenance report records adopted and rejected candidates plus production-line metrics. |
| Bounded AWS operational state | Botocore service contracts above plus internal provider-error taxonomy | EMR requests at most 50 applications or job runs per page; application discovery stops above 100 pages or 1,000 records, and bulk job-run discovery stops above 100 pages. User-driven Glue Catalog, Glue Jobs, Glue Crawlers, and EMR job-run collections stop at 1,000 items through a bounded VMx token-pager specialization. Athena history, saved-query, and context collections stop at 1,000 items, while result rows stop at 10,000, through an app-owned bounded snapshot pager that retains VMx commands. A visible, snapshot-stable safety-limit state replaces the ordinary load-more affordance when a ceiling ends pagination. Log discovery classifies only the run-relative suffix so user prefixes cannot impersonate worker markers. Recursive S3 deletion stops above 100 listing pages or 10,000 objects and reports how many objects were already removed. S3 SlowDown and RequestLimitExceeded responses map to ThrottledError; service availability and transport failures map to ProviderUnreachableError. |
Exact request-shape and pager tests assert page size, page ceilings, collection ceilings, final-page clipping, refresh and snapshot preservation, visible truncation state, partial-delete diagnostics, repeated-token rejection, run-relative log classification, and throttling versus reachability behavior. |
| Bounded local and cross-provider state | Internal InMemoryFS and CrossFs contracts |
Demo objects are limited to 100 MiB each and 256 MiB in aggregate, and a directory listing fails closed above 10,000 entries. Recursive cross-provider copy and move traversals stop above 10,000 entries or 128 levels; owned staging data is cleaned after a limit failure. | In-memory filesystem boundary tests cover known-size and streamed writes, overwrite accounting, and listing ceilings. Cross-filesystem tests cover entry/depth rejection and stage cleanup. |
| Packaging and developer tooling | hatchling==1.32.0, testcontainers==4.15.0, and textual-dev==1.8.0 from uv.lock; build-system.requires constrained to hatchling>=1.31.0,<2 |
CI, release, Pages, and bootstrap sync/export with --locked, so a stale lock fails instead of silently installing it. Bootstrap installs all dependency groups. The Textual development CLI used by scripts/dev.sh is declared explicitly. Wheel and sdist members must exclude repository metadata, tests, local caches, and traversal paths; both artifacts must contain every source Python module, py.typed, and packaged theme stylesheet, while the sdist must also retain its build metadata and PyPI readme. |
uv lock --check, script/workflow guard tests, real wheel/sdist builds, scripts.check_dist, Twine metadata checks, isolated wheel and sdist install smoke, and a Textual CLI invocation. |
| GitHub Actions and pre-commit toolchain | astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d (v10.0.1), ruff-pre-commit@321478e58f4938179c6b86e4ddfa923d1547a49b (v0.16.6), pre-commit-hooks@3e8a8703264a2f4a69428a0aa4dcb512790b2c8c (v6.0.0), taplo-pre-commit@ade0f95ddcf661c697d4670d2cfcbe95d0048a0a (v0.9.3), and the other immutable action refs listed in §4 |
Workflow jobs retain least-privilege permissions and bounded timeouts. CI and release export every locked dependency group before pip-audit. Pages and wiki publication run only from main, including manual dispatch, so a branch-controlled checkout cannot receive the wiki deploy key. The wiki deploy key is checked before it is written. The Homebrew checkout does not persist its cross-repository token. Artifact contents are checked before upload or publication. |
Official tag/ref verification, YAML guard tests, pre-commit, shellcheck, and local workflow-equivalent package/docs commands. |
| Local S3-compatible harness | Docker image adobe/s3mock:5.2.0@sha256:7a37f0d796e81a28b970c892dcae532797014616b3312b467af8f0274ebf0c26; testcontainers==4.15.0 from uv.lock |
Independent S3-compatible endpoint, readiness probe, seeded buckets/objects, path-style config, arbitrary test credentials, and host port exposure. | The former MinIO community image was removed because every usable community release is affected by GHSA-hv4r-mvr4-25vw, while the nominally fixed AIStor image requires a commercial license and rejects writes without one. Adobe S3Mock 5.2.0 is license-free, digest-pinned, exposed only on 127.0.0.1, and passes the same nine strict S3FS/CrossFs protocol tests through the public testcontainers.core.container.DockerContainer API. Manual trace also covers scripts/test-services/s3/docker-compose.yml, seed.py, and config-snippet.toml. |
Exact S3 boto operation ledger (16):
AbortMultipartUpload
CompleteMultipartUpload
CopyObject
CreateBucket
CreateMultipartUpload
DeleteObject
DeleteObjects
GetObject
GetObjectTagging
HeadBucket
HeadObject
ListBuckets
ListObjectsV2
PutObject
UploadPart
UploadPartCopy
7. 2026-09-17 dependency maintenance pass¶
Carries §6 forward at new pins. Only the three refs below moved; every other
contract, boundary, and verification method is unchanged and restated here so
this section is the single authoritative record of what is currently in force.
astral-sh/setup-uv v10.0.1 → v10.1.0, ruff-pre-commit v0.16.6 → v0.16.8,
and the S3Mock harness image 5.2.0 → 5.2.2. The action and hook refs were
verified against the upstream tags with git ls-remote rather than taken from
the bumps alone, and the S3Mock digest was verified against the Docker Hub
manifest for the 5.2.2 tag. The hook ref was taken one patch past the
dependabot proposal so it matches the locked ruff==0.16.8 the lint tier runs.
The same pass moved the unledgered development-only cairosvg, fonttools,
and ruff lock entries. Supersedes dependabot #214, #215, #216, #217, and
218, which could not update this ledger themselves and so could never go¶
green; #217 was additionally rejected because its grouped
pytest-textual-snapshot 1.1.0 bump forces pytest 8 and syrupy 4, a downgrade.
| Integration point | Pinned version / ref | Consumed contract | Verification method |
|---|---|---|---|
| Python runtime and AWS SDK graph | aioboto3==15.5.0, aiobotocore==2.25.1, botocore==1.40.61, textual==8.2.8, vmx==3.23.0, reactivex==5.1.0, rich==15.0.0, keyring==25.7.0, tomli-w==1.2.0, platformdirs==4.11.8, sqlglot==30.18.0, anyio==4.15.1, and aiofiles==25.1.0 from uv.lock |
VMx and the other runtime packages use public facades or documented module paths. The Textual compatibility adapter uses exact-version private hooks (_bindings, _pre_process, _handle_exception, Screen._clear_tooltip, and _xterm_parser.XTermParser) because Textual 8.2.8 has no equivalent public binding-replacement, lifecycle-recovery, tooltip-dismissal, or input-parser surface; Screen._clear_tooltip is the routine Textual itself calls from Screen._on_screen_suspend, and it is invoked here on AppBlur, which Textual 8.2.8 leaves uncovered. The same AppBlur handler releases a held mouse capture through the public App.capture_mouse(None): App._watch_app_focus drops focus without it, and ScrollBar._on_mouse_capture pairs App._realtime_animation_begin — which calls gc.disable() under PAUSE_GC_ON_SCROLL — only with a MouseRelease. XTermParser is subclassed for the bracketed-paste guard: XTermParser.parse strands bracketed_paste set forever when the closing \x1b[201~ is split by more than constants.ESCAPE_DELAY, because the ParseTimeout path abandons the partial sequence without returning its bytes to paste_buffer, so the app keeps repainting while every key is swallowed. App.get_driver_class therefore wraps the selected driver, and the wrapper rebinds the module-level XTermParser name each Textual driver builds its parser from (linux_driver.py:426, linux_inline_driver.py:129, web_driver.py:187, win32.py:230) rather than forking an input loop; only feed, tick and parse_mouse_code are overridden. Bracketed paste stays enabled. textual.constants.SMOOTH_SCROLL is additionally rebound to False in main() before App.run whenever the user has not exported TEXTUAL_SMOOTH_SCROLL: constants reads the environment once at import time so setting the variable after aws_tui.app has imported Textual does nothing, while _xterm_parser.py:319-323 resolves the name through the module at parse time. That flag has exactly one use site in Textual 8.2.8 — acceptance of the mode_id == "2048" in-band window-resize report — so clearing it restores the SIGWINCH fallback that drivers/linux_driver.py:246-248 makes a no-op once in-band resize is negotiated. The branch it gates reaches one hop further, and the cost is named rather than assumed to be nil: App.supports_smooth_scrolling is assigned only from the resulting InBandWindowResize message (app.py:5016-5019) and read only at scrollbar.py:395, so a scrollbar drag animates towards the pointer instead of tracking it; and LinuxDriver._enable_mouse_pixels (linux_driver.py:474-482, writing \x1b[?1016h at :145-150) is reachable only from that branch, so mouse coordinates stay cell-granular. Both losses are Textual's own behaviour on iTerm2 and Terminal.app, which never negotiate 2048, and on Windows, whose driver has no in-band resize path. The in-band resize report handler (_xterm_parser.py:271-283) sets mouse_pixels unconditionally — gated on neither SMOOTH_SCROLL nor IS_ITERM — so a mode 2048 left set by an earlier app would otherwise flip the parser into pixel coordinates that mode 1016 never negotiated, dividing every SGR click by the pixel/cell ratio; CellMouseXTermParser.parse_mouse_code keeps the two halves in step. An explicit TEXTUAL_SMOOTH_SCROLL wins in either direction. The exact Textual pin prevents unreviewed drift, and is the runtime requirement pyproject.toml declares, not merely a development constraint: uv pip install --resolution lowest-direct . resolves textual==8.2.8, so the shipped floor and the tested version are the same. VMx owns command admission/cancellation, modal focus restoration, immutable form construction, observable state, component lifecycle, filtering, and paging. AWS operations and request members are validated against the locked Botocore models. |
Full unit, integration, snapshot, and E2E tiers; minimum-direct-dependency tests; import/layer checks; tests/integration/test_app_blur_cleanup.py pins the tooltip and mouse-capture teardown on AppBlur and the balanced realtime-animation count; tests/unit/ui/test_paste_guard.py reproduces the upstream wedge against the stock parser and pins the module-level parser name the swap depends on, and tests/integration/test_paste_guard_recovery.py drives split-marker bytes into a live app and asserts it still answers a key; tests/unit/ui/test_terminal_protocol.py reproduces both the upstream 2048 negotiation and the stale-mode pixel-coordinate collapse against the stock parser, pins the SIGWINCH gate, the single SMOOTH_SCROLL use site and the two indirect consumers of the branch it gates (app.py → scrollbar.py, and _enable_mouse_pixels → \x1b[?1016h) in the installed package, and asserts the default is settled before App.run; VMx compatibility regressions for command cancellation, retired pager generations, form validation, and modal restoration; source-derived Botocore operation and input-member tests. |
| VMx 3.23 compatibility and specialization | vmx==3.23.0 from uv.lock; runtime requirement vmx>=3.23.0,<4.0.0 |
FocusCoordinatorVM delegates modal save/restore behavior to public DiscriminatorVM.modal_open() / modal_close(). S3ConnectionFormVM supplies complete field and model validation through FormVMBuilder at construction time. Athena drains public AsyncRelayCommand.is_executing admission state after cancellation and tracks the provider task behind nested command execution so shutdown waits for cancellation-resistant I/O. No VM reaches into VMx private fields. |
Focus, Settings form, Athena query/results, VMx smoke, mypy, and lifecycle tests run against the locked package. The dated VMx 3.23 maintenance report records adopted and rejected candidates plus production-line metrics. |
| Bounded AWS operational state | Botocore service contracts above plus internal provider-error taxonomy | EMR requests at most 50 applications or job runs per page; application discovery stops above 100 pages or 1,000 records, and bulk job-run discovery stops above 100 pages. User-driven Glue Catalog, Glue Jobs, Glue Crawlers, and EMR job-run collections stop at 1,000 items through a bounded VMx token-pager specialization. Athena history, saved-query, and context collections stop at 1,000 items, while result rows stop at 10,000, through an app-owned bounded snapshot pager that retains VMx commands. A visible, snapshot-stable safety-limit state replaces the ordinary load-more affordance when a ceiling ends pagination. Log discovery classifies only the run-relative suffix so user prefixes cannot impersonate worker markers. Recursive S3 deletion stops above 100 listing pages or 10,000 objects and reports how many objects were already removed. S3 SlowDown and RequestLimitExceeded responses map to ThrottledError; service availability and transport failures map to ProviderUnreachableError. |
Exact request-shape and pager tests assert page size, page ceilings, collection ceilings, final-page clipping, refresh and snapshot preservation, visible truncation state, partial-delete diagnostics, repeated-token rejection, run-relative log classification, and throttling versus reachability behavior. |
| Bounded local and cross-provider state | Internal InMemoryFS and CrossFs contracts |
Demo objects are limited to 100 MiB each and 256 MiB in aggregate, and a directory listing fails closed above 10,000 entries. Recursive cross-provider copy and move traversals stop above 10,000 entries or 128 levels; owned staging data is cleaned after a limit failure. | In-memory filesystem boundary tests cover known-size and streamed writes, overwrite accounting, and listing ceilings. Cross-filesystem tests cover entry/depth rejection and stage cleanup. |
| Packaging and developer tooling | hatchling==1.32.0, testcontainers==4.15.0, and textual-dev==1.8.0 from uv.lock; build-system.requires constrained to hatchling>=1.31.0,<2 |
CI, release, Pages, and bootstrap sync/export with --locked, so a stale lock fails instead of silently installing it. Bootstrap installs all dependency groups. The Textual development CLI used by scripts/dev.sh is declared explicitly. Wheel and sdist members must exclude repository metadata, tests, local caches, and traversal paths; both artifacts must contain every source Python module, py.typed, and packaged theme stylesheet, while the sdist must also retain its build metadata and PyPI readme. |
uv lock --check, script/workflow guard tests, real wheel/sdist builds, scripts.check_dist, Twine metadata checks, isolated wheel and sdist install smoke, and a Textual CLI invocation. |
| GitHub Actions and pre-commit toolchain | astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 (v10.1.0), ruff-pre-commit@2eeb5678de71a00c0902cbda7105d328432f72cb (v0.16.8), pre-commit-hooks@3e8a8703264a2f4a69428a0aa4dcb512790b2c8c (v6.0.0), taplo-pre-commit@ade0f95ddcf661c697d4670d2cfcbe95d0048a0a (v0.9.3), and the other immutable action refs listed in §4 |
Workflow jobs retain least-privilege permissions and bounded timeouts. CI and release export every locked dependency group before pip-audit. Pages and wiki publication run only from main, including manual dispatch, so a branch-controlled checkout cannot receive the wiki deploy key. The wiki deploy key is checked before it is written. The Homebrew checkout does not persist its cross-repository token. Artifact contents are checked before upload or publication. |
Official tag/ref verification, YAML guard tests, pre-commit, shellcheck, and local workflow-equivalent package/docs commands. |
| Local S3-compatible harness | Docker image adobe/s3mock:5.2.2@sha256:e7c36014dcf4c7f0f6bec9de888477d1d9b8f55eceb7d9c1186f70d7aadf81ca; testcontainers==4.15.0 from uv.lock |
Independent S3-compatible endpoint, readiness probe, seeded buckets/objects, path-style config, arbitrary test credentials, and host port exposure. | The former MinIO community image was removed because every usable community release is affected by GHSA-hv4r-mvr4-25vw, while the nominally fixed AIStor image requires a commercial license and rejects writes without one. Adobe S3Mock 5.2.2 is license-free, digest-pinned, exposed only on 127.0.0.1, and passes the same nine strict S3FS/CrossFs protocol tests through the public testcontainers.core.container.DockerContainer API. Manual trace also covers scripts/test-services/s3/docker-compose.yml, seed.py, and config-snippet.toml. |
| Local DuckDB Iceberg preview | duckdb==1.5.5 from uv.lock, optional extra duckdb |
Extensions httpfs, aws, iceberg loaded per connection. iceberg_scan(path) with the named parameter snapshot_from_id. CREATE OR REPLACE SECRET (TYPE s3, PROVIDER credential_chain, PROFILE, REGION). The setting unsafe_enable_version_guessing. DuckDBPyConnection.interrupt() for cancellation. Errors consumed as duckdb.HTTPException with .status_code, and duckdb.InterruptException. Eager credential validation is a 1.4+ refinement, not a floor guarantee: at 1.3 CREATE OR REPLACE SECRET accepts a nonexistent profile, so a bad profile degrades to a later, less precise outcome instead of AUTH_REQUIRED. |
Port unit tests drive an injected fake connection; no test spawns a real engine. A floor test under tests/minimum_runtime/ exercises the real package when the extra is installed. |
Exact S3 boto operation ledger (16):
AbortMultipartUpload
CompleteMultipartUpload
CopyObject
CreateBucket
CreateMultipartUpload
DeleteObject
DeleteObjects
GetObject
GetObjectTagging
HeadBucket
HeadObject
ListBuckets
ListObjectsV2
PutObject
UploadPart
UploadPartCopy
8. 2026-09-26 dependency maintenance pass¶
Carries §7 forward at new pins. Only the two refs below moved; every other
contract, boundary, and verification method is unchanged and restated here so
this section is the single authoritative record of what is currently in force.
platformdirs 4.11.8 → 4.11.10 and hatchling 1.32.0 → 1.32.3. Both are patch
bumps of packages consumed through public APIs: platformdirs resolves
config/cache paths behind src/aws_tui/infra/paths.py, and hatchling is the
PEP 517 backend, exercised by the real wheel and sdist builds rather than taken
from the bump alone. Each was pinned to exactly the version its dependabot PR
proposed: uv lock --upgrade-package offers platformdirs 4.12.0 and
hatchling 1.32.4, and the former is a minor bump of the package that resolves
this app's config and cache directories, so it belongs in its own reviewed
change rather than folded in here. Supersedes dependabot #269 and #270, which
could not update this ledger themselves and so could never go green.
This section also records one new integration point rather than a moved pin: the local DuckDB Iceberg preview, added with the duckdb optional extra. Its row is carried in the table below.
| Integration point | Pinned version / ref | Consumed contract | Verification method |
|---|---|---|---|
| Python runtime and AWS SDK graph | aioboto3==15.5.0, aiobotocore==2.25.1, botocore==1.40.61, textual==8.2.8, vmx==3.23.0, reactivex==5.1.0, rich==15.0.0, keyring==25.7.0, tomli-w==1.2.0, platformdirs==4.11.10, sqlglot==30.18.0, anyio==4.15.1, and aiofiles==25.1.0 from uv.lock |
VMx and the other runtime packages use public facades or documented module paths. The Textual compatibility adapter uses exact-version private hooks (_bindings, _pre_process, _handle_exception, Screen._clear_tooltip, and _xterm_parser.XTermParser) because Textual 8.2.8 has no equivalent public binding-replacement, lifecycle-recovery, tooltip-dismissal, or input-parser surface; Screen._clear_tooltip is the routine Textual itself calls from Screen._on_screen_suspend, and it is invoked here on AppBlur, which Textual 8.2.8 leaves uncovered. The same AppBlur handler releases a held mouse capture through the public App.capture_mouse(None): App._watch_app_focus drops focus without it, and ScrollBar._on_mouse_capture pairs App._realtime_animation_begin — which calls gc.disable() under PAUSE_GC_ON_SCROLL — only with a MouseRelease. XTermParser is subclassed for the bracketed-paste guard: XTermParser.parse strands bracketed_paste set forever when the closing \x1b[201~ is split by more than constants.ESCAPE_DELAY, because the ParseTimeout path abandons the partial sequence without returning its bytes to paste_buffer, so the app keeps repainting while every key is swallowed. App.get_driver_class therefore wraps the selected driver, and the wrapper rebinds the module-level XTermParser name each Textual driver builds its parser from (linux_driver.py:426, linux_inline_driver.py:129, web_driver.py:187, win32.py:230) rather than forking an input loop; only feed, tick and parse_mouse_code are overridden. Bracketed paste stays enabled. textual.constants.SMOOTH_SCROLL is additionally rebound to False in main() before App.run whenever the user has not exported TEXTUAL_SMOOTH_SCROLL: constants reads the environment once at import time so setting the variable after aws_tui.app has imported Textual does nothing, while _xterm_parser.py:319-323 resolves the name through the module at parse time. That flag has exactly one use site in Textual 8.2.8 — acceptance of the mode_id == "2048" in-band window-resize report — so clearing it restores the SIGWINCH fallback that drivers/linux_driver.py:246-248 makes a no-op once in-band resize is negotiated. The branch it gates reaches one hop further, and the cost is named rather than assumed to be nil: App.supports_smooth_scrolling is assigned only from the resulting InBandWindowResize message (app.py:5016-5019) and read only at scrollbar.py:395, so a scrollbar drag animates towards the pointer instead of tracking it; and LinuxDriver._enable_mouse_pixels (linux_driver.py:474-482, writing \x1b[?1016h at :145-150) is reachable only from that branch, so mouse coordinates stay cell-granular. Both losses are Textual's own behaviour on iTerm2 and Terminal.app, which never negotiate 2048, and on Windows, whose driver has no in-band resize path. The in-band resize report handler (_xterm_parser.py:271-283) sets mouse_pixels unconditionally — gated on neither SMOOTH_SCROLL nor IS_ITERM — so a mode 2048 left set by an earlier app would otherwise flip the parser into pixel coordinates that mode 1016 never negotiated, dividing every SGR click by the pixel/cell ratio; CellMouseXTermParser.parse_mouse_code keeps the two halves in step. An explicit TEXTUAL_SMOOTH_SCROLL wins in either direction. The exact Textual pin prevents unreviewed drift, and is the runtime requirement pyproject.toml declares, not merely a development constraint: uv pip install --resolution lowest-direct . resolves textual==8.2.8, so the shipped floor and the tested version are the same. VMx owns command admission/cancellation, modal focus restoration, immutable form construction, observable state, component lifecycle, filtering, and paging. AWS operations and request members are validated against the locked Botocore models. |
Full unit, integration, snapshot, and E2E tiers; minimum-direct-dependency tests; import/layer checks; tests/integration/test_app_blur_cleanup.py pins the tooltip and mouse-capture teardown on AppBlur and the balanced realtime-animation count; tests/unit/ui/test_paste_guard.py reproduces the upstream wedge against the stock parser and pins the module-level parser name the swap depends on, and tests/integration/test_paste_guard_recovery.py drives split-marker bytes into a live app and asserts it still answers a key; tests/unit/ui/test_terminal_protocol.py reproduces both the upstream 2048 negotiation and the stale-mode pixel-coordinate collapse against the stock parser, pins the SIGWINCH gate, the single SMOOTH_SCROLL use site and the two indirect consumers of the branch it gates (app.py → scrollbar.py, and _enable_mouse_pixels → \x1b[?1016h) in the installed package, and asserts the default is settled before App.run; VMx compatibility regressions for command cancellation, retired pager generations, form validation, and modal restoration; source-derived Botocore operation and input-member tests. |
| VMx 3.23 compatibility and specialization | vmx==3.23.0 from uv.lock; runtime requirement vmx>=3.23.0,<4.0.0 |
FocusCoordinatorVM delegates modal save/restore behavior to public DiscriminatorVM.modal_open() / modal_close(). S3ConnectionFormVM supplies complete field and model validation through FormVMBuilder at construction time. Athena drains public AsyncRelayCommand.is_executing admission state after cancellation and tracks the provider task behind nested command execution so shutdown waits for cancellation-resistant I/O. No VM reaches into VMx private fields. |
Focus, Settings form, Athena query/results, VMx smoke, mypy, and lifecycle tests run against the locked package. The dated VMx 3.23 maintenance report records adopted and rejected candidates plus production-line metrics. |
| Bounded AWS operational state | Botocore service contracts above plus internal provider-error taxonomy | EMR requests at most 50 applications or job runs per page; application discovery stops above 100 pages or 1,000 records, and bulk job-run discovery stops above 100 pages. User-driven Glue Catalog, Glue Jobs, Glue Crawlers, and EMR job-run collections stop at 1,000 items through a bounded VMx token-pager specialization. Athena history, saved-query, and context collections stop at 1,000 items, while result rows stop at 10,000, through an app-owned bounded snapshot pager that retains VMx commands. A visible, snapshot-stable safety-limit state replaces the ordinary load-more affordance when a ceiling ends pagination. Log discovery classifies only the run-relative suffix so user prefixes cannot impersonate worker markers. Recursive S3 deletion stops above 100 listing pages or 10,000 objects and reports how many objects were already removed. S3 SlowDown and RequestLimitExceeded responses map to ThrottledError; service availability and transport failures map to ProviderUnreachableError. |
Exact request-shape and pager tests assert page size, page ceilings, collection ceilings, final-page clipping, refresh and snapshot preservation, visible truncation state, partial-delete diagnostics, repeated-token rejection, run-relative log classification, and throttling versus reachability behavior. |
| Bounded local and cross-provider state | Internal InMemoryFS and CrossFs contracts |
Demo objects are limited to 100 MiB each and 256 MiB in aggregate, and a directory listing fails closed above 10,000 entries. Recursive cross-provider copy and move traversals stop above 10,000 entries or 128 levels; owned staging data is cleaned after a limit failure. | In-memory filesystem boundary tests cover known-size and streamed writes, overwrite accounting, and listing ceilings. Cross-filesystem tests cover entry/depth rejection and stage cleanup. |
| Packaging and developer tooling | hatchling==1.32.3, testcontainers==4.15.0, and textual-dev==1.8.0 from uv.lock; build-system.requires constrained to hatchling>=1.31.0,<2 |
CI, release, Pages, and bootstrap sync/export with --locked, so a stale lock fails instead of silently installing it. Bootstrap installs all dependency groups. The Textual development CLI used by scripts/dev.sh is declared explicitly. Wheel and sdist members must exclude repository metadata, tests, local caches, and traversal paths; both artifacts must contain every source Python module, py.typed, and packaged theme stylesheet, while the sdist must also retain its build metadata and PyPI readme. |
uv lock --check, script/workflow guard tests, real wheel/sdist builds, scripts.check_dist, Twine metadata checks, isolated wheel and sdist install smoke, and a Textual CLI invocation. |
| GitHub Actions and pre-commit toolchain | astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 (v10.1.0), ruff-pre-commit@2eeb5678de71a00c0902cbda7105d328432f72cb (v0.16.8), pre-commit-hooks@3e8a8703264a2f4a69428a0aa4dcb512790b2c8c (v6.0.0), taplo-pre-commit@ade0f95ddcf661c697d4670d2cfcbe95d0048a0a (v0.9.3), and the other immutable action refs listed in §4 |
Workflow jobs retain least-privilege permissions and bounded timeouts. CI and release export every locked dependency group before pip-audit. Pages and wiki publication run only from main, including manual dispatch, so a branch-controlled checkout cannot receive the wiki deploy key. The wiki deploy key is checked before it is written. The Homebrew checkout does not persist its cross-repository token. Artifact contents are checked before upload or publication. |
Official tag/ref verification, YAML guard tests, pre-commit, shellcheck, and local workflow-equivalent package/docs commands. |
| Local S3-compatible harness | Docker image adobe/s3mock:5.2.2@sha256:e7c36014dcf4c7f0f6bec9de888477d1d9b8f55eceb7d9c1186f70d7aadf81ca; testcontainers==4.15.0 from uv.lock |
Independent S3-compatible endpoint, readiness probe, seeded buckets/objects, path-style config, arbitrary test credentials, and host port exposure. | The former MinIO community image was removed because every usable community release is affected by GHSA-hv4r-mvr4-25vw, while the nominally fixed AIStor image requires a commercial license and rejects writes without one. Adobe S3Mock 5.2.2 is license-free, digest-pinned, exposed only on 127.0.0.1, and passes the same nine strict S3FS/CrossFs protocol tests through the public testcontainers.core.container.DockerContainer API. Manual trace also covers scripts/test-services/s3/docker-compose.yml, seed.py, and config-snippet.toml. |
| Local DuckDB Iceberg preview | duckdb==1.5.5 from uv.lock, optional extra duckdb |
Extensions httpfs, aws, iceberg loaded per connection. iceberg_scan(path) with the named parameter snapshot_from_id. CREATE OR REPLACE SECRET (TYPE s3, PROVIDER credential_chain, PROFILE, REGION). The setting unsafe_enable_version_guessing. DuckDBPyConnection.interrupt() for cancellation. Errors consumed as duckdb.HTTPException with .status_code, and duckdb.InterruptException. Eager credential validation is a 1.4+ refinement, not a floor guarantee: at 1.3 CREATE OR REPLACE SECRET accepts a nonexistent profile, so a bad profile degrades to a later, less precise outcome instead of AUTH_REQUIRED. |
Port unit tests drive an injected fake connection; no test spawns a real engine. A floor test under tests/minimum_runtime/ exercises the real package when the extra is installed. |
Exact S3 boto operation ledger (16):
AbortMultipartUpload
CompleteMultipartUpload
CopyObject
CreateBucket
CreateMultipartUpload
DeleteObject
DeleteObjects
GetObject
GetObjectTagging
HeadBucket
HeadObject
ListBuckets
ListObjectsV2
PutObject
UploadPart
UploadPartCopy
9. Known model gaps¶
| Consumed contract | Pinned version | What the model does not offer | Consequence |
|---|---|---|---|
glue:GetJobRuns state filter |
botocore==1.40.61 service model, Glue API 2017-03-31 |
GetJobRunsRequest models exactly JobName, MaxResults, NextToken. There is no States member, so the server-side run-state filter GlueClient.list_job_runs_page probes for is permanently unavailable. |
Run-state filtering falls back to filtering one 200-run page locally, so a state with no match on the current page renders an empty list while matching runs may sit on the next one. The user can advance that pager: l (glue.load_more), the Load more Glue rows palette command, or clicking the runs footer that reads "· more available" fetches the next page, so matching older runs are reachable by paging rather than by filter. tests/docs/test_contract_parity.py::test_glue_get_job_runs_still_has_no_modeled_states_filter fails the moment AWS adds the member, at which point the probe goes live and this row can be removed. |
emr-serverless:StartJobRun idempotency |
botocore==1.40.61 service model, EMR Serverless API 2021-07-13 |
clientToken is a required, idempotencyToken: true member; the SDK auto-fills a fresh UUID per call, not per user intent. |
aws-tui supplies its own token: JobRunCloneVM mints one per form intent, reuses it across submit attempts that raise, and rotates it on a field edit or a successful submit, so a retry after an accepted-but-lost request, while the clone form stays open, returns the existing run instead of starting a second one; cancelling and reopening the form is a new intent with a new token. tests/unit/vm/emr_serverless/test_clone_vm.py covers reuse and rotation; tests/unit/domain/test_emr_serverless.py asserts the wire field. The Athena path supplies ClientRequestToken the same way. |
10. Deferred contract checks¶
- External upstream documentation was not exhaustively re-queried for every library API. The concrete code paths above were checked against the locked dependency graph and strengthened with tests where this pass changed behavior.
11. EMR clone request fidelity¶
Verified against installed botocore==1.40.61, EMR Serverless API 2021-07-13,
on 2026-09-30 by service-model inspection, exact request tests and live modal
rendering tests. GetJobRun retains the full driver discriminator and optional
configurationOverrides (application and monitoring configuration),
executionTimeoutMinutes, retryPolicy, mode, executionIamPolicy and
tags. The explicit StartJobRun builder forwards only modeled request fields,
preserves supplied strings, omitted versus empty arguments, empty containers and zero separately from omission,
and deep-copies nested values. Only Spark drivers and BATCH/STREAMING modes are
accepted. Unknown or unsupported nested settings fail closed; newer AWS API
fields absent from this installed model are not silently discarded.
Source application release/network/image/worker values are review context only.
The clone inherits current application settings and discloses unknown equality
and hidden defaults; response identifiers, state and resource usage are never
resubmitted. The review shows source identity and old/new values before Submit. JSON editors
escape Unicode separators; arguments accept null for omission and Spark
parameters use a JSON string or null to preserve mixed line endings.
Every request-affecting edit rotates the intent token; equal reapplication and
unchanged ambiguous retries retain it. Submission errors suppress raw SDK text
and exception chains so diagnostics cannot echo request values.
Evidence: tests/unit/domain/test_emr_serverless.py,
tests/unit/vm/emr_serverless/test_clone_vm.py,
tests/unit/ui/emr_serverless/test_clone_modal.py,
tests/integration/test_emr_page.py and
tests/snapshot/test_emr_clone_modal.py. These use stubs and demo data; they do
not establish permissions or successful execution in a live AWS account.