gnat_flyology_native 13.2.0-patchset.1.1.1
GNAT native compiler with a validated gnat-patches aggregate
- Origin
- Platform-specific binary archives
Index activity
Crates and versions added or updated, derived from the repository history for packages currently in the index.
158 manifest changes
69 new versions
89 development updates
0c8956fa on GitHub
13.2.0-patchset.1.1.1GNAT native compiler with a validated gnat-patches aggregate
14.2.0-patchset.1.1.1GNAT native compiler with a validated gnat-patches aggregate
15.3.0-patchset.1.1.1GNAT native compiler with a validated gnat-patches aggregate
16.1.0-patchset.1.1.1GNAT native compiler with a validated gnat-patches aggregate
16.2.0-patchset.1.1.1GNAT native compiler with a validated gnat-patches aggregate
cbe19910 on GitHub
0.2.0Experimental GNAT runtime extension for native and lightweight Ada tasks
flyology_allocators ~0.1.0gnat >=13 & <1748dafe34 on GitHub
0.1.0Caller-owned memory allocation algorithms for Ada
gnat >=13 & <177e008273 on GitHub
0.1.1Fast CRC-16, CRC-32, and CRC-64 algorithms for Ada
gnat >=13 & <178124bfd3 on GitHub
0.1.0Fast CRC-16, CRC-32, and CRC-64 algorithms for Ada
gnat >=13 & <17122abd7e on GitHub
0.1.0Aggregate command-line tooling for Flyology projects
gnat >=13 & <17b287bf8f on GitHub
0.1.0-devAggregate command-line tooling for Flyology projects
gnat >=13 & <170.1.0-devS3 client and pluggable object-storage server for Flyology
f28a3fb6 → updated to 2c7efddaindex 8a7129511 source commit
2c7efddaListBucketIntelligentTieringConfigurations is tracked by the pinned S3 model but lacks executable client and signed-corpus coverage. Implementing its continuation, bounded XML decoding, composable ownership, and evidence registration independently would repeat the same paginated read mechanics and leave future operations vulnerable to drift.
Solution: Qualify Intelligent-Tiering through the paginated REST/XML family
Add the model-verified Intelligent-Tiering page decoder and provider-centric synchronous and composable calls over the shared state machine. Register reviewed absence, continuation, exclusion, signed-socket, generated-negative, qualification, coverage, and documentation evidence while keeping page traversal caller-owned and preserving existing ownership, cancellation, bounds, and no-retry semantics.
2c7efdda → updated to 77811f75index 20236e511 source commit
77811f75ListBucketInventoryConfigurations is tracked by the pinned S3 model but has no provider-owned one-page client or executable qualification. Callers cannot use the reviewed paginated configuration-read state machine for inventory pages, and the coverage ledger remains partial.
Solution: qualify inventory pages through the shared family
Add the strict page projection, exact low-level request and decoder, provider-owned synchronous and composable overloads, and typed Finish over the existing bounded state machine. Record the reviewed decisions in the operation manifest and regenerate model-derived negative XML, signed-socket, coverage, test-registration, and documentation evidence.
77811f75 → updated to 21a19c53index 6c16e56d1 source commit
21a19c53GetBucketMetadataConfiguration is inventoried but lacks an executable provider-owned client, strict complete model projection, and generated qualification evidence. Callers cannot compose or synchronously read the current metadata destination and table state through the shared owner-driven transport, and the coverage ledger remains partial.
Solution: qualify the complete bucket metadata read
Add strict model-preserving metadata result types and decoding, exact low-level request, execute, and composable forms, and provider-owned limited, restart, Finish, and synchronous overloads over Bounded_REST_XML_Reads. Record reviewed endpoint and error policy and generate signed and negative evidence for the complete reachable shape graph.
21a19c53 → updated to d2781ffcindex cf3488ae1 source commit
d2781ffcThe pinned S3 model tracks ListObjectAnnotations, but the client and signed
corpus remain partial. The shared paginated REST/XML family only accepts
flattened items and fixed page scalars, which would otherwise force this
wrapper- and header-bearing object read to duplicate transport ownership and
qualification plumbing.
Solution: add ListObjectAnnotations through the shared read families
Extend the internal paginated and bounded read families with explicit reviewed
container, scalar, and same-response header hooks. Add the strict annotation
codec, exact low-level preparation, provider-owned synchronous and composable
forms, model-generated negative cases, and signed socket qualification. Update
the evidence registry and greedy URI-label verifier while preserving caller-
selected limits, read-only certainty, restart ownership, and typed Finish.
d2781ffc → updated to 6fb5f95aindex 841989482 source commits
d5d4cbb2Each REST/XML mutation currently duplicates one-shot source handling, bounded response collection, child drain, cancellation, restart, and Finish mechanics. The signed-socket registry also assumes bodyless requests, so new mutation qualification requires bespoke synchronization and cannot verify the exact signed payload hash.
Solution: Add shared nonreplaying mutation infrastructure
Introduce an internal generic that centralizes only transport and ownership mechanics while leaving serialization, admission classification, reconciliation, and public policy in reviewed provider actuals. Extend the pinned-model signed-socket generator to derive and verify request bodies and payload hashes, reject collisions with derived headers, and support mutation plans without response-XML negative templates.
6fb5f95aPutBucketMetricsConfiguration has no exact prepared, synchronous, or provider-owned composable API despite its Get/List/Delete siblings being qualified. PutBucketReplication also retains a private copy of the same mutation lifetime machinery, leaving the new family without a retrospective equivalence canary.
Solution: Qualify metrics replacement through the shared family
Add strict bounded metrics serialization, exact id-query preparation, one-shot low-level execution, and provider-centric Set_Metrics_Configuration overloads with typed certainty and caller-directed reconciliation. Migrate PutBucketReplication onto the shared family without changing its public API or normalization, and add model-derived deterministic and signed-socket evidence for low-level, synchronous, composable, restart, duplicate-header, and bounded-response lanes.
6fb5f95a → updated to 1193aa33index d0d3e6ee1 source commit
1193aa33PutBucketAnalyticsConfiguration has no provider-owned operation, typed Finish path, or executable signed qualification. Callers cannot compose analytics replacement with the existing Get/List/Delete vocabulary, and the shared mutation preparer incorrectly conflates a required present-empty configuration id with absence.
Solution: Route analytics replacement through the shared mutation family
Add exact bounded analytics serialization, operation-specific low-level preparation and pre-admission checks, and provider-owned limited, restartable, typed, and synchronous forms over the existing one-shot REST/XML mutation state machine. Preserve conservative admission certainty and read-only reconciliation, retain present-empty modeled ids, and register deterministic and signed-socket evidence without introducing execution-policy defaults.
1193aa33 → updated to 0bf21f8cindex 033ebb981 source commit
0bf21f8cCallers cannot use the intelligent-tiering configuration mutation through the provider-owned synchronous and composable API, and sibling analytics and metrics mutation qualifiers collapse conclusive authentication, authorization, and absence failures into a generic invalid-request result.
Solution: add the shared-family mutation operation and preserve typed rejections
Add strict intelligent-tiering XML serialization, low-level preparation, provider-centric synchronous/composable/restart/Finish lanes over the shared mutation state machine, signed and deterministic executable evidence, and generated coverage registration. Keep the request body one-shot and non-rewindable, prohibit replay, preserve conservative outcome certainty, and correct the sibling signed qualifiers to retain conclusive typed failures.
0bf21f8c → updated to 76e39f44index 8b626e558 source commits
08c974f8Future routine operations still require handwritten XML structure validation and lack one lossless operation-level inventory of implementation mode, generation eligibility, and executable evidence. That keeps review spread across many files and risks treating declarations as coverage.
Solution: add strict codec generation and a reviewed generation registry
Upgrade the 116-operation registry without changing existing Ada implementations, add deterministic inventory and documentation outputs, introduce a private shared strict XML runtime with caller-owned limits, and teach the uv scaffold to emit guarded generated descriptors only after human decisions are resolved.
b2dd0fa6A prospective generator can be deterministic while still drifting from the reviewed provider API, strict Website codec, typed Finish lanes, or executable evidence. Replacing the existing handwritten operation would hide that distinction.
Solution: verify prospective Website output against authoritative sources
Generate a normalized GetBucketWebsite canary that hashes the untouched handwritten implementation, compares model, XML-negative, signed-socket, API, lifecycle, and coverage evidence, and records prospective descriptor hashes without committing or adopting that descriptor.
634f81b8Each bounded REST/XML read carries another Ada executable that repeats environment decoding, client setup, completion-set driving, typed Finish, restart, cancellation drain, and shutdown. This expands handwritten review surface even though only preparation and typed result observation differ by operation.
Solution: Add a shared typed signed-socket read driver
Introduce a test-only generic that owns the invariant low-level, synchronous, composable, restart, cancellation/drain/reuse, and cleanup lanes while retaining operation-specific typed callbacks. Allow the loopback runner to target a prospective adapter without replacing the committed authoritative adapter, and document the generated-versus-human boundary.
197ab0e4The retrospective registry marks GetBucketLifecycle and GetBucketNotification as generator candidates even though the pinned model deprecates them and the maintained compatibility contract names their Configuration replacements. A scaffold could therefore create public APIs the repository intentionally excludes.
Solution: Record the deprecated-operation exclusions
Mark both legacy reads ineligible, remove their unresolved generation blocks, and regenerate the inventory, counts, and documentation without changing their preserved partial coverage state or generated-model provenance.
490d7660New S3 operations can carry Botocore string-pattern constraints that the strict descriptor runtime does not represent. Silently accepting those values would make generated codecs weaker than the pinned model and could admit malformed provider responses.
Solution: Add fail-closed exact pattern validation
Teach the generator and shared strict runtime to validate the modeled S3 and S3 Express bucket ARN domain, reject all unsupported pattern forms during generation, document the boundary, and cover the directory-bucket descriptor plus the no-enum generation edge case. Regenerated canary evidence records the expanded strict contract without replacing any handwritten operation.
b17b81cdNew operations must add public declarations and bodies to provider-centric parent packages, but treating those large packages as generated output would make existing reviewed operations vulnerable to unrelated churn.
Solution: Materialize only delimited generated regions
Add a deterministic source-region materializer that inserts or replaces one uniquely balanced generated block while preserving every surrounding byte. Reject missing anchors, duplicate markers, and unbalanced regions, cover insertion and replacement with negative oracles, and document the handwritten/generated ownership boundary.
5d8a6921The bounded REST/XML read family transports document limits but has no place for the distinct caller-selected collection limit required by generated list codecs. Copying its owner-driven lifecycle into every list operation would preserve the repeated mechanical work the automation is intended to remove.
Solution: Add a shared paginated read family
Add one provider-neutral family that retains the bounded response sink, child lifetime, cancellation, drain, restart, exception, and typed Finish behavior while carrying an explicit collection limit into response decoding. It introduces no defaults and changes no existing operation.
76e39f44ListDirectoryBuckets is absent from the client even though its request and strict XML response mechanics are derivable from the pinned Botocore model. Implementing it by hand would repeat low-level, provider, lifecycle, corpus, socket, documentation, and coverage bookkeeping across another large review surface.
Solution: Generate and qualify ListDirectoryBuckets through the paginated read family
Extend the authoritative generator to materialize exact low-level and provider-owned Ada regions, a strict bounded XML codec, and deterministic evidence from reviewed manifest decisions. Add the shared typed signed-socket adapter, verify mismatched prepared operations before admission, preserve caller-selected limits and owner-driven restart/cancellation semantics, and register executable coverage without claiming backend or server support.
76e39f44 → updated to eb2e25e9index fd29616c2 source commits
44636497New S3 mutation serializers need strict escaping and caller-selected document, depth, element, and text enforcement. Repeating that machinery in every operation would keep the review surface large and could let model changes silently retain stale typed projections.
Solution: Add a shared bounded XML writer and model-bound serializer projections
The internal writer centralizes escaping and explicit resource-limit accounting without defaults. The generator now verifies full reachable-shape digests before emitting reviewed Inventory, Logging, and Website request projections, with deterministic negative oracles and maintainer documentation.
eb2e25e9New REST/XML configuration mutations still require large handwritten low-level, provider, serializer, and signed-socket slices even when their transport and lifecycle semantics match the reviewed shared family. This keeps the remaining S3 backlog expensive to implement and review.
Solution: Generate three reviewed configuration mutations
Generate PutBucketInventoryConfiguration, PutBucketLogging, and PutBucketWebsite serializers, exact prepared-operation bindings, provider-centric synchronous/composable/restart APIs, typed Finish results, and typed qualification adapters from one authoritative tool. Keep certainty, no-replay, reconciliation, ownership, cross-field, checksum, and compatibility decisions explicit in the human-owned registry while expanding one reviewed fixture into the common signed matrix.
eb2e25e9 → updated to 5eaf79cfindex 7a7549171 source commit
5eaf79cfFuture S3 ACL mutation coverage repeats model-derived request serialization, prepared-request binding, provider state-machine glue, and signed-socket qualification. That review surface slows the remaining backlog and risks drift between synchronous, composable, and restart lanes.
Solution: generate PutBucketAcl from reviewed model and policy inputs
Extend the deterministic mutation generator with strict ACL XML serialization, modeled headers and checksums, exact-operation admission, provider-centric Set_ACL overloads, typed Finish, and shared qualification evidence. Keep ACL mode selection, certainty, no-replay, reconciliation, cross-field exclusions, and ownership decisions explicit in the human-owned operation manifest.
0.1.0-devSupervised S3 server and management console for Flyology Object Storage
f28a3fb6 → updated to 2c7efddaindex 8a7129511 source commit
2c7efddaListBucketIntelligentTieringConfigurations is tracked by the pinned S3 model but lacks executable client and signed-corpus coverage. Implementing its continuation, bounded XML decoding, composable ownership, and evidence registration independently would repeat the same paginated read mechanics and leave future operations vulnerable to drift.
Solution: Qualify Intelligent-Tiering through the paginated REST/XML family
Add the model-verified Intelligent-Tiering page decoder and provider-centric synchronous and composable calls over the shared state machine. Register reviewed absence, continuation, exclusion, signed-socket, generated-negative, qualification, coverage, and documentation evidence while keeping page traversal caller-owned and preserving existing ownership, cancellation, bounds, and no-retry semantics.
2c7efdda → updated to 77811f75index 20236e511 source commit
77811f75ListBucketInventoryConfigurations is tracked by the pinned S3 model but has no provider-owned one-page client or executable qualification. Callers cannot use the reviewed paginated configuration-read state machine for inventory pages, and the coverage ledger remains partial.
Solution: qualify inventory pages through the shared family
Add the strict page projection, exact low-level request and decoder, provider-owned synchronous and composable overloads, and typed Finish over the existing bounded state machine. Record the reviewed decisions in the operation manifest and regenerate model-derived negative XML, signed-socket, coverage, test-registration, and documentation evidence.
77811f75 → updated to 21a19c53index 6c16e56d1 source commit
21a19c53GetBucketMetadataConfiguration is inventoried but lacks an executable provider-owned client, strict complete model projection, and generated qualification evidence. Callers cannot compose or synchronously read the current metadata destination and table state through the shared owner-driven transport, and the coverage ledger remains partial.
Solution: qualify the complete bucket metadata read
Add strict model-preserving metadata result types and decoding, exact low-level request, execute, and composable forms, and provider-owned limited, restart, Finish, and synchronous overloads over Bounded_REST_XML_Reads. Record reviewed endpoint and error policy and generate signed and negative evidence for the complete reachable shape graph.
21a19c53 → updated to d2781ffcindex cf3488ae1 source commit
d2781ffcThe pinned S3 model tracks ListObjectAnnotations, but the client and signed
corpus remain partial. The shared paginated REST/XML family only accepts
flattened items and fixed page scalars, which would otherwise force this
wrapper- and header-bearing object read to duplicate transport ownership and
qualification plumbing.
Solution: add ListObjectAnnotations through the shared read families
Extend the internal paginated and bounded read families with explicit reviewed
container, scalar, and same-response header hooks. Add the strict annotation
codec, exact low-level preparation, provider-owned synchronous and composable
forms, model-generated negative cases, and signed socket qualification. Update
the evidence registry and greedy URI-label verifier while preserving caller-
selected limits, read-only certainty, restart ownership, and typed Finish.
d2781ffc → updated to 6fb5f95aindex 841989482 source commits
d5d4cbb2Each REST/XML mutation currently duplicates one-shot source handling, bounded response collection, child drain, cancellation, restart, and Finish mechanics. The signed-socket registry also assumes bodyless requests, so new mutation qualification requires bespoke synchronization and cannot verify the exact signed payload hash.
Solution: Add shared nonreplaying mutation infrastructure
Introduce an internal generic that centralizes only transport and ownership mechanics while leaving serialization, admission classification, reconciliation, and public policy in reviewed provider actuals. Extend the pinned-model signed-socket generator to derive and verify request bodies and payload hashes, reject collisions with derived headers, and support mutation plans without response-XML negative templates.
6fb5f95aPutBucketMetricsConfiguration has no exact prepared, synchronous, or provider-owned composable API despite its Get/List/Delete siblings being qualified. PutBucketReplication also retains a private copy of the same mutation lifetime machinery, leaving the new family without a retrospective equivalence canary.
Solution: Qualify metrics replacement through the shared family
Add strict bounded metrics serialization, exact id-query preparation, one-shot low-level execution, and provider-centric Set_Metrics_Configuration overloads with typed certainty and caller-directed reconciliation. Migrate PutBucketReplication onto the shared family without changing its public API or normalization, and add model-derived deterministic and signed-socket evidence for low-level, synchronous, composable, restart, duplicate-header, and bounded-response lanes.
6fb5f95a → updated to 1193aa33index d0d3e6ee1 source commit
1193aa33PutBucketAnalyticsConfiguration has no provider-owned operation, typed Finish path, or executable signed qualification. Callers cannot compose analytics replacement with the existing Get/List/Delete vocabulary, and the shared mutation preparer incorrectly conflates a required present-empty configuration id with absence.
Solution: Route analytics replacement through the shared mutation family
Add exact bounded analytics serialization, operation-specific low-level preparation and pre-admission checks, and provider-owned limited, restartable, typed, and synchronous forms over the existing one-shot REST/XML mutation state machine. Preserve conservative admission certainty and read-only reconciliation, retain present-empty modeled ids, and register deterministic and signed-socket evidence without introducing execution-policy defaults.
1193aa33 → updated to 0bf21f8cindex 033ebb981 source commit
0bf21f8cCallers cannot use the intelligent-tiering configuration mutation through the provider-owned synchronous and composable API, and sibling analytics and metrics mutation qualifiers collapse conclusive authentication, authorization, and absence failures into a generic invalid-request result.
Solution: add the shared-family mutation operation and preserve typed rejections
Add strict intelligent-tiering XML serialization, low-level preparation, provider-centric synchronous/composable/restart/Finish lanes over the shared mutation state machine, signed and deterministic executable evidence, and generated coverage registration. Keep the request body one-shot and non-rewindable, prohibit replay, preserve conservative outcome certainty, and correct the sibling signed qualifiers to retain conclusive typed failures.
0bf21f8c → updated to 76e39f44index 8b626e558 source commits
08c974f8Future routine operations still require handwritten XML structure validation and lack one lossless operation-level inventory of implementation mode, generation eligibility, and executable evidence. That keeps review spread across many files and risks treating declarations as coverage.
Solution: add strict codec generation and a reviewed generation registry
Upgrade the 116-operation registry without changing existing Ada implementations, add deterministic inventory and documentation outputs, introduce a private shared strict XML runtime with caller-owned limits, and teach the uv scaffold to emit guarded generated descriptors only after human decisions are resolved.
b2dd0fa6A prospective generator can be deterministic while still drifting from the reviewed provider API, strict Website codec, typed Finish lanes, or executable evidence. Replacing the existing handwritten operation would hide that distinction.
Solution: verify prospective Website output against authoritative sources
Generate a normalized GetBucketWebsite canary that hashes the untouched handwritten implementation, compares model, XML-negative, signed-socket, API, lifecycle, and coverage evidence, and records prospective descriptor hashes without committing or adopting that descriptor.
634f81b8Each bounded REST/XML read carries another Ada executable that repeats environment decoding, client setup, completion-set driving, typed Finish, restart, cancellation drain, and shutdown. This expands handwritten review surface even though only preparation and typed result observation differ by operation.
Solution: Add a shared typed signed-socket read driver
Introduce a test-only generic that owns the invariant low-level, synchronous, composable, restart, cancellation/drain/reuse, and cleanup lanes while retaining operation-specific typed callbacks. Allow the loopback runner to target a prospective adapter without replacing the committed authoritative adapter, and document the generated-versus-human boundary.
197ab0e4The retrospective registry marks GetBucketLifecycle and GetBucketNotification as generator candidates even though the pinned model deprecates them and the maintained compatibility contract names their Configuration replacements. A scaffold could therefore create public APIs the repository intentionally excludes.
Solution: Record the deprecated-operation exclusions
Mark both legacy reads ineligible, remove their unresolved generation blocks, and regenerate the inventory, counts, and documentation without changing their preserved partial coverage state or generated-model provenance.
490d7660New S3 operations can carry Botocore string-pattern constraints that the strict descriptor runtime does not represent. Silently accepting those values would make generated codecs weaker than the pinned model and could admit malformed provider responses.
Solution: Add fail-closed exact pattern validation
Teach the generator and shared strict runtime to validate the modeled S3 and S3 Express bucket ARN domain, reject all unsupported pattern forms during generation, document the boundary, and cover the directory-bucket descriptor plus the no-enum generation edge case. Regenerated canary evidence records the expanded strict contract without replacing any handwritten operation.
b17b81cdNew operations must add public declarations and bodies to provider-centric parent packages, but treating those large packages as generated output would make existing reviewed operations vulnerable to unrelated churn.
Solution: Materialize only delimited generated regions
Add a deterministic source-region materializer that inserts or replaces one uniquely balanced generated block while preserving every surrounding byte. Reject missing anchors, duplicate markers, and unbalanced regions, cover insertion and replacement with negative oracles, and document the handwritten/generated ownership boundary.
5d8a6921The bounded REST/XML read family transports document limits but has no place for the distinct caller-selected collection limit required by generated list codecs. Copying its owner-driven lifecycle into every list operation would preserve the repeated mechanical work the automation is intended to remove.
Solution: Add a shared paginated read family
Add one provider-neutral family that retains the bounded response sink, child lifetime, cancellation, drain, restart, exception, and typed Finish behavior while carrying an explicit collection limit into response decoding. It introduces no defaults and changes no existing operation.
76e39f44ListDirectoryBuckets is absent from the client even though its request and strict XML response mechanics are derivable from the pinned Botocore model. Implementing it by hand would repeat low-level, provider, lifecycle, corpus, socket, documentation, and coverage bookkeeping across another large review surface.
Solution: Generate and qualify ListDirectoryBuckets through the paginated read family
Extend the authoritative generator to materialize exact low-level and provider-owned Ada regions, a strict bounded XML codec, and deterministic evidence from reviewed manifest decisions. Add the shared typed signed-socket adapter, verify mismatched prepared operations before admission, preserve caller-selected limits and owner-driven restart/cancellation semantics, and register executable coverage without claiming backend or server support.
76e39f44 → updated to eb2e25e9index fd29616c2 source commits
44636497New S3 mutation serializers need strict escaping and caller-selected document, depth, element, and text enforcement. Repeating that machinery in every operation would keep the review surface large and could let model changes silently retain stale typed projections.
Solution: Add a shared bounded XML writer and model-bound serializer projections
The internal writer centralizes escaping and explicit resource-limit accounting without defaults. The generator now verifies full reachable-shape digests before emitting reviewed Inventory, Logging, and Website request projections, with deterministic negative oracles and maintainer documentation.
eb2e25e9New REST/XML configuration mutations still require large handwritten low-level, provider, serializer, and signed-socket slices even when their transport and lifecycle semantics match the reviewed shared family. This keeps the remaining S3 backlog expensive to implement and review.
Solution: Generate three reviewed configuration mutations
Generate PutBucketInventoryConfiguration, PutBucketLogging, and PutBucketWebsite serializers, exact prepared-operation bindings, provider-centric synchronous/composable/restart APIs, typed Finish results, and typed qualification adapters from one authoritative tool. Keep certainty, no-replay, reconciliation, ownership, cross-field, checksum, and compatibility decisions explicit in the human-owned registry while expanding one reviewed fixture into the common signed matrix.
eb2e25e9 → updated to 5eaf79cfindex 7a7549171 source commit
5eaf79cfFuture S3 ACL mutation coverage repeats model-derived request serialization, prepared-request binding, provider state-machine glue, and signed-socket qualification. That review surface slows the remaining backlog and risks drift between synchronous, composable, and restart lanes.
Solution: generate PutBucketAcl from reviewed model and policy inputs
Extend the deterministic mutation generator with strict ACL XML serialization, modeled headers and checksums, exact-operation admission, provider-centric Set_ACL overloads, typed Finish, and shared qualification evidence. Keep ACL mode selection, certainty, no-replay, reconciliation, cross-field exclusions, and ownership decisions explicit in the human-owned operation manifest.
0.1.0-devSQLite catalog backend for Flyology Object Storage
f28a3fb6 → updated to 2c7efddaindex 8a7129511 source commit
2c7efddaListBucketIntelligentTieringConfigurations is tracked by the pinned S3 model but lacks executable client and signed-corpus coverage. Implementing its continuation, bounded XML decoding, composable ownership, and evidence registration independently would repeat the same paginated read mechanics and leave future operations vulnerable to drift.
Solution: Qualify Intelligent-Tiering through the paginated REST/XML family
Add the model-verified Intelligent-Tiering page decoder and provider-centric synchronous and composable calls over the shared state machine. Register reviewed absence, continuation, exclusion, signed-socket, generated-negative, qualification, coverage, and documentation evidence while keeping page traversal caller-owned and preserving existing ownership, cancellation, bounds, and no-retry semantics.
2c7efdda → updated to 77811f75index 20236e511 source commit
77811f75ListBucketInventoryConfigurations is tracked by the pinned S3 model but has no provider-owned one-page client or executable qualification. Callers cannot use the reviewed paginated configuration-read state machine for inventory pages, and the coverage ledger remains partial.
Solution: qualify inventory pages through the shared family
Add the strict page projection, exact low-level request and decoder, provider-owned synchronous and composable overloads, and typed Finish over the existing bounded state machine. Record the reviewed decisions in the operation manifest and regenerate model-derived negative XML, signed-socket, coverage, test-registration, and documentation evidence.
77811f75 → updated to 21a19c53index 6c16e56d1 source commit
21a19c53GetBucketMetadataConfiguration is inventoried but lacks an executable provider-owned client, strict complete model projection, and generated qualification evidence. Callers cannot compose or synchronously read the current metadata destination and table state through the shared owner-driven transport, and the coverage ledger remains partial.
Solution: qualify the complete bucket metadata read
Add strict model-preserving metadata result types and decoding, exact low-level request, execute, and composable forms, and provider-owned limited, restart, Finish, and synchronous overloads over Bounded_REST_XML_Reads. Record reviewed endpoint and error policy and generate signed and negative evidence for the complete reachable shape graph.
21a19c53 → updated to d2781ffcindex cf3488ae1 source commit
d2781ffcThe pinned S3 model tracks ListObjectAnnotations, but the client and signed
corpus remain partial. The shared paginated REST/XML family only accepts
flattened items and fixed page scalars, which would otherwise force this
wrapper- and header-bearing object read to duplicate transport ownership and
qualification plumbing.
Solution: add ListObjectAnnotations through the shared read families
Extend the internal paginated and bounded read families with explicit reviewed
container, scalar, and same-response header hooks. Add the strict annotation
codec, exact low-level preparation, provider-owned synchronous and composable
forms, model-generated negative cases, and signed socket qualification. Update
the evidence registry and greedy URI-label verifier while preserving caller-
selected limits, read-only certainty, restart ownership, and typed Finish.
d2781ffc → updated to 6fb5f95aindex 841989482 source commits
d5d4cbb2Each REST/XML mutation currently duplicates one-shot source handling, bounded response collection, child drain, cancellation, restart, and Finish mechanics. The signed-socket registry also assumes bodyless requests, so new mutation qualification requires bespoke synchronization and cannot verify the exact signed payload hash.
Solution: Add shared nonreplaying mutation infrastructure
Introduce an internal generic that centralizes only transport and ownership mechanics while leaving serialization, admission classification, reconciliation, and public policy in reviewed provider actuals. Extend the pinned-model signed-socket generator to derive and verify request bodies and payload hashes, reject collisions with derived headers, and support mutation plans without response-XML negative templates.
6fb5f95aPutBucketMetricsConfiguration has no exact prepared, synchronous, or provider-owned composable API despite its Get/List/Delete siblings being qualified. PutBucketReplication also retains a private copy of the same mutation lifetime machinery, leaving the new family without a retrospective equivalence canary.
Solution: Qualify metrics replacement through the shared family
Add strict bounded metrics serialization, exact id-query preparation, one-shot low-level execution, and provider-centric Set_Metrics_Configuration overloads with typed certainty and caller-directed reconciliation. Migrate PutBucketReplication onto the shared family without changing its public API or normalization, and add model-derived deterministic and signed-socket evidence for low-level, synchronous, composable, restart, duplicate-header, and bounded-response lanes.
6fb5f95a → updated to 1193aa33index d0d3e6ee1 source commit
1193aa33PutBucketAnalyticsConfiguration has no provider-owned operation, typed Finish path, or executable signed qualification. Callers cannot compose analytics replacement with the existing Get/List/Delete vocabulary, and the shared mutation preparer incorrectly conflates a required present-empty configuration id with absence.
Solution: Route analytics replacement through the shared mutation family
Add exact bounded analytics serialization, operation-specific low-level preparation and pre-admission checks, and provider-owned limited, restartable, typed, and synchronous forms over the existing one-shot REST/XML mutation state machine. Preserve conservative admission certainty and read-only reconciliation, retain present-empty modeled ids, and register deterministic and signed-socket evidence without introducing execution-policy defaults.
1193aa33 → updated to 0bf21f8cindex 033ebb981 source commit
0bf21f8cCallers cannot use the intelligent-tiering configuration mutation through the provider-owned synchronous and composable API, and sibling analytics and metrics mutation qualifiers collapse conclusive authentication, authorization, and absence failures into a generic invalid-request result.
Solution: add the shared-family mutation operation and preserve typed rejections
Add strict intelligent-tiering XML serialization, low-level preparation, provider-centric synchronous/composable/restart/Finish lanes over the shared mutation state machine, signed and deterministic executable evidence, and generated coverage registration. Keep the request body one-shot and non-rewindable, prohibit replay, preserve conservative outcome certainty, and correct the sibling signed qualifiers to retain conclusive typed failures.
0bf21f8c → updated to 76e39f44index 8b626e558 source commits
08c974f8Future routine operations still require handwritten XML structure validation and lack one lossless operation-level inventory of implementation mode, generation eligibility, and executable evidence. That keeps review spread across many files and risks treating declarations as coverage.
Solution: add strict codec generation and a reviewed generation registry
Upgrade the 116-operation registry without changing existing Ada implementations, add deterministic inventory and documentation outputs, introduce a private shared strict XML runtime with caller-owned limits, and teach the uv scaffold to emit guarded generated descriptors only after human decisions are resolved.
b2dd0fa6A prospective generator can be deterministic while still drifting from the reviewed provider API, strict Website codec, typed Finish lanes, or executable evidence. Replacing the existing handwritten operation would hide that distinction.
Solution: verify prospective Website output against authoritative sources
Generate a normalized GetBucketWebsite canary that hashes the untouched handwritten implementation, compares model, XML-negative, signed-socket, API, lifecycle, and coverage evidence, and records prospective descriptor hashes without committing or adopting that descriptor.
634f81b8Each bounded REST/XML read carries another Ada executable that repeats environment decoding, client setup, completion-set driving, typed Finish, restart, cancellation drain, and shutdown. This expands handwritten review surface even though only preparation and typed result observation differ by operation.
Solution: Add a shared typed signed-socket read driver
Introduce a test-only generic that owns the invariant low-level, synchronous, composable, restart, cancellation/drain/reuse, and cleanup lanes while retaining operation-specific typed callbacks. Allow the loopback runner to target a prospective adapter without replacing the committed authoritative adapter, and document the generated-versus-human boundary.
197ab0e4The retrospective registry marks GetBucketLifecycle and GetBucketNotification as generator candidates even though the pinned model deprecates them and the maintained compatibility contract names their Configuration replacements. A scaffold could therefore create public APIs the repository intentionally excludes.
Solution: Record the deprecated-operation exclusions
Mark both legacy reads ineligible, remove their unresolved generation blocks, and regenerate the inventory, counts, and documentation without changing their preserved partial coverage state or generated-model provenance.
490d7660New S3 operations can carry Botocore string-pattern constraints that the strict descriptor runtime does not represent. Silently accepting those values would make generated codecs weaker than the pinned model and could admit malformed provider responses.
Solution: Add fail-closed exact pattern validation
Teach the generator and shared strict runtime to validate the modeled S3 and S3 Express bucket ARN domain, reject all unsupported pattern forms during generation, document the boundary, and cover the directory-bucket descriptor plus the no-enum generation edge case. Regenerated canary evidence records the expanded strict contract without replacing any handwritten operation.
b17b81cdNew operations must add public declarations and bodies to provider-centric parent packages, but treating those large packages as generated output would make existing reviewed operations vulnerable to unrelated churn.
Solution: Materialize only delimited generated regions
Add a deterministic source-region materializer that inserts or replaces one uniquely balanced generated block while preserving every surrounding byte. Reject missing anchors, duplicate markers, and unbalanced regions, cover insertion and replacement with negative oracles, and document the handwritten/generated ownership boundary.
5d8a6921The bounded REST/XML read family transports document limits but has no place for the distinct caller-selected collection limit required by generated list codecs. Copying its owner-driven lifecycle into every list operation would preserve the repeated mechanical work the automation is intended to remove.
Solution: Add a shared paginated read family
Add one provider-neutral family that retains the bounded response sink, child lifetime, cancellation, drain, restart, exception, and typed Finish behavior while carrying an explicit collection limit into response decoding. It introduces no defaults and changes no existing operation.
76e39f44ListDirectoryBuckets is absent from the client even though its request and strict XML response mechanics are derivable from the pinned Botocore model. Implementing it by hand would repeat low-level, provider, lifecycle, corpus, socket, documentation, and coverage bookkeeping across another large review surface.
Solution: Generate and qualify ListDirectoryBuckets through the paginated read family
Extend the authoritative generator to materialize exact low-level and provider-owned Ada regions, a strict bounded XML codec, and deterministic evidence from reviewed manifest decisions. Add the shared typed signed-socket adapter, verify mismatched prepared operations before admission, preserve caller-selected limits and owner-driven restart/cancellation semantics, and register executable coverage without claiming backend or server support.
76e39f44 → updated to eb2e25e9index fd29616c2 source commits
44636497New S3 mutation serializers need strict escaping and caller-selected document, depth, element, and text enforcement. Repeating that machinery in every operation would keep the review surface large and could let model changes silently retain stale typed projections.
Solution: Add a shared bounded XML writer and model-bound serializer projections
The internal writer centralizes escaping and explicit resource-limit accounting without defaults. The generator now verifies full reachable-shape digests before emitting reviewed Inventory, Logging, and Website request projections, with deterministic negative oracles and maintainer documentation.
eb2e25e9New REST/XML configuration mutations still require large handwritten low-level, provider, serializer, and signed-socket slices even when their transport and lifecycle semantics match the reviewed shared family. This keeps the remaining S3 backlog expensive to implement and review.
Solution: Generate three reviewed configuration mutations
Generate PutBucketInventoryConfiguration, PutBucketLogging, and PutBucketWebsite serializers, exact prepared-operation bindings, provider-centric synchronous/composable/restart APIs, typed Finish results, and typed qualification adapters from one authoritative tool. Keep certainty, no-replay, reconciliation, ownership, cross-field, checksum, and compatibility decisions explicit in the human-owned registry while expanding one reviewed fixture into the common signed matrix.
eb2e25e9 → updated to 5eaf79cfindex 7a7549171 source commit
5eaf79cfFuture S3 ACL mutation coverage repeats model-derived request serialization, prepared-request binding, provider state-machine glue, and signed-socket qualification. That review surface slows the remaining backlog and risks drift between synchronous, composable, and restart lanes.
Solution: generate PutBucketAcl from reviewed model and policy inputs
Extend the deterministic mutation generator with strict ACL XML serialization, modeled headers and checksums, exact-operation admission, provider-centric Set_ACL overloads, typed Finish, and shared qualification evidence. Keep ACL mode selection, certainty, no-replay, reconciliation, cross-field exclusions, and ownership decisions explicit in the human-owned operation manifest.
0.1.0-devS3 client and pluggable object-storage server for Flyology
6ec20483 → updated to 735fb607index a47fabdb25 source commits
94c0ee9cThe exact lifecycle deletion remains limited to blocking convenience and prepared execution. Callers cannot own it in a completion set, preserve typed admission certainty, or reuse its state without a parallel wrapper.
Solution: add the provider-owned lifecycle deletion operation
Add the exact low-level initiator and colocated Client.Buckets constructor, operation-last initiation, typed Finish, and synchronous wait over one non-replaying state machine. Gate certainty, prepared-operation identity, physical headers, response bounds, restart, native/lightweight execution, and document the provider inventory.
571522b3The existing convenience client executes synchronously through its own path, so callers cannot compose the exact signed policy-status read with other provider operations or reuse its operation state safely.
Solution: colocate a bounded composable policy-status operation
Add the exact low-level initiator, provider-owned limited constructor, operation-last restart, typed Finish, and typed synchronous wait. Drive the convenience overload through the same bounded state machine, reject malformed singleton headers and over-limit bodies, and qualify the result mapping and native/lightweight socket behavior.
1c0455b9Requester-payment reads are available only through the blocking executor, so structured callers cannot own their deadline, cancellation, operation reuse, typed HTTP outcome, and bounded response lifecycle alongside other work.
Solution: Colocate a bounded composable requester-payment read
Add exact prepared-request admission, provider-owned limited construction and operation-last restart, typed Finish and synchronous wait, strict header and response bounds, full normalization and loopback corpora, and maintained API documentation. The established convenience call now waits on the same state machine without adding retry or billing-policy interpretation.
6befc49aThe synchronous requester-payment mutation owns no reusable operation, exposes no admission certainty, and cannot participate in a caller-owned completion set.
Solution: add a non-replaying provider-owned requester-payment operation
Add the exact low-level initiator, limited constructor, operation-last restart, typed Finish and synchronous wait over one state machine. Own the signed payer bytes through terminal drain, preserve strict response bounds and headers, classify ambiguous outcomes without replay, and gate the contract with certainty, socket, model, documentation and repository checks.
4664482fCallers can only execute PutBucketAbac synchronously, so owner-driven state machines cannot retain typed publication certainty or reconcile ambiguous outcomes without a parallel adapter.
Solution: add a provider-owned non-replaying ABAC operation
Expose exact low-level initiation and Client.Buckets limited-constructor, operation-last, typed Finish, and synchronous-wait forms over one state machine. Preserve the serialized status as a one-shot owned source, normalize admission certainty, extend exact-operation and socket corpora, and document the provider inventory and reconciliation contract.
cc04da27Callers can only execute the acceleration mutation synchronously, so owner-driven workflows cannot preserve typed admission certainty or reconcile ambiguous publication without a separate adapter.
Solution: add the provider-owned acceleration operation
Expose the exact one-shot low-level initiator and Client.Buckets limited-constructor, operation-last, typed Finish, and synchronous-wait forms over one state machine. Preserve the model's no-Content-MD5 rule, normalize mutation certainty, extend exact-operation and socket corpora, and document read reconciliation and the updated provider inventory.
062f8119Callers can read the optional ABAC state only through a blocking convenience call, so a possibly admitted PutBucketAbac cannot compose its read-only reconciliation with other owner-driven operations. The low-level prepared-request boundary also has no exact reusable initiator for this operation.
Solution: add one provider-owned bounded GetBucketAbac state machine
Add the exact Low_Level initiator, limited constructor, operation-last reusable form, typed Finish, and typed synchronous wait in Client.Buckets. Drive the convenience call through that same state machine, preserve the caller-selected XML bound and inherited defaults, and qualify exact operation binding, restart, response normalization, singleton headers, limits, documentation, and ownership without adding retry or helper tasks.
59ea351bCallers that need to reconcile PutBucketAccelerateConfiguration outcomes can only use the blocking convenience read. The exact prepared request has no reusable initiator, so owner-driven workflows cannot compose the bounded response without introducing a parallel state machine.
Solution: colocate a bounded acceleration read with the Buckets provider
Add the exact Low_Level initiator, limited constructor, operation-last restart, typed Finish, and typed synchronous wait. Drive the existing convenience overload through the same provider-owned state machine, enforce caller-selected response bounds and singleton headers, and qualify request binding, restart, normalization, malformed headers, and overflow without retries or helper tasks.
45564e81GetBucketAcl has a strict typed low-level synchronous client and an authenticated server route, but no provider-owned limited operation. Callers cannot coordinate ACL reads with completion-set workflows without introducing a parallel execution path.
Solution: colocate a bounded ACL read with the Buckets provider
Add the exact Low_Level initiator, limited constructor, operation-last restart, typed Finish, and typed synchronous wait. Retain the signed request and bounded response through terminal drain, preserve physical singleton headers and admission diagnostics, and qualify exact binding, restart, normalization, and response limits without retries or helper tasks.
6b4e9836GetObjectAcl has a strict typed synchronous client and authenticated server route, but it lacks the provider-owned limited operation needed by caller-driven completion sets. Callers otherwise need a second API shape and cannot reuse the exact signed request, bounded response, admission certainty, or failure mapping in a larger state machine.
Solution: colocate a bounded ACL read with the Objects provider
Add the exact Low_Level sink initiator and Client.Objects limited constructor, operation-last restart, typed Finish, and synchronous wait over one owner-driven state machine. Preserve exact version, requester-pays, expected-owner, singleton-header, caller-limit, cancellation, drain, and no-retry behavior, and gate the surface with preparation, restart, failure-normalization, socket, documentation, and full deterministic corpora.
22a5e68dGetBucketMetadataTableConfiguration has strict preparation, decoding, and synchronous execution, but no provider-owned limited operation. Callers cannot compose the exact signed request, bounded response snapshot, admission certainty, or typed failure mapping into a larger completion-set state machine.
Solution: colocate the bounded read with the Buckets provider
Add the exact Low_Level sink initiator and Client.Buckets limited constructor, operation-last restart, typed Finish, and synchronous wait over one owner-driven state machine. Preserve the opaque provider status, expected-owner projection, singleton diagnostics, caller XML limits, cancellation, drain, and no-retry behavior, and gate the surface with exact preparation, restart, normalization, socket, documentation, and full deterministic corpora.
0d0dbbacCallers can prepare and synchronously execute CreateBucketMetadataTableConfiguration, but they cannot initiate, restart, or consume it from a provider-owned completion set. The replayable prepared body also obscures the mutation certainty and read-before-retry contract after possible admission.
Solution: add one-shot provider-owned metadata-table creation
Move the prepared payload onto the existing non-rewindable owned-source path, add exact low-level admission gating, and colocate the limited constructor, operation-last restart, typed Finish, and typed synchronous wait in Client.Buckets. Preserve signed MD5/checksum and owner projection, expose conservative mutation certainty, require read-only reconciliation for unknown outcomes, and gate ownership, bounds, restart, cross-operation rejection, and the complete certainty matrix.
c457fcd8DeleteObjectAnnotation is limited to a synchronous low-level call, so owner-driven object workflows cannot schedule it beside other provider operations or retain typed admission certainty through terminal drain.
Solution: colocate nonreplaying annotation deletion with objects
Add an exact low-level initiator and provider-owned constructor, operation-last restart, typed Finish, and synchronous wait over one state machine. Copy all request identity, preserve one-shot mutation semantics, normalize publication certainty conservatively, and gate the API with exhaustive mapping and signed socket coverage.
f1cd6026DeleteBucketReplication is available only through synchronous convenience calls, so owner-driven callers cannot retain typed admission certainty, reuse an established operation, or compose it with sibling work.
Solution: colocate composable replication deletion with the bucket provider
Add the exact low-level initiator and provider-owned limited operation, operation-last initiation, typed Finish, and typed synchronous wait. Drive the convenience form through the same non-replaying state machine and gate its certainty, ownership, bounds, wire behavior, and documentation exhaustively.
068b0a9aDeleteBucketWebsite is available only through synchronous convenience calls, so owner-driven callers cannot retain typed admission certainty, reuse an established operation, or compose it with sibling work.
Solution: colocate composable website deletion with the bucket provider
Add the exact low-level initiator and provider-owned limited operation, operation-last initiation, typed Finish, and typed synchronous wait. Drive the convenience form through the same non-replaying state machine and gate its certainty, ownership, bounds, wire behavior, and documentation exhaustively.
7fd0ffc1DeleteBucketInventoryConfiguration is available only through a blocking convenience call, so callers cannot schedule it with other owner-driven operations or retain typed publication certainty after possible admission.
Solution: add a provider-owned composable inventory deletion
Add the exact low-level initiator, limited constructor, operation-last restart, typed Finish and synchronous wait over one non-replaying state machine. Extend identifier-copy, certainty, response-bound, signed socket, verifier, API documentation and compatibility evidence.
d71a924dDirectory-bucket callers must use a blocking scalar wrapper, cannot schedule the exchange beside other operations, and cannot inspect typed transport failure or admission certainty. The limited zeroizing credentials result also cannot safely live inside reusable operation storage.
Solution: add a provider-owned composable CreateSession
Retain the signed request, physically validated response metadata, and bounded raw response until typed Finish constructs credentials exactly once. Add same-name limited and operation-last forms, make both synchronous forms wait on the same state machine, and qualify cancellation, restart, request copying, response bounds, and malformed-response normalization.
55460971DeleteBucketIntelligentTieringConfiguration is available only through the synchronous bucket client, so callers cannot place it in a shared completion set or inspect typed admission certainty after an ambiguous mutation result.
Solution: add provider-owned composable intelligent-tiering deletion
Add exact low-level initiation, a non-replaying Client.Buckets operation with operation-last restart and typed Finish, and route the synchronous forms through the same state machine. Qualify copied inputs, certainty normalization, response bounds, malformed headers, documentation, and the pinned operation inventory.
4bf62837DeleteBucketAnalyticsConfiguration is available only through the synchronous bucket client, so callers cannot share a completion set or retain typed certainty when deletion may have reached the service.
Solution: add provider-owned composable analytics deletion
Add an exact low-level initiator, a non-replaying Client.Buckets operation with copied request inputs, operation-last restart, typed Finish, and a synchronous wait over the same state machine. Extend the pinned verifier, certainty matrix, signed native/lightweight socket corpus, and maintained API documentation.
d50be24cDeleteBucketMetricsConfiguration is available only through the synchronous bucket client, so callers cannot share a completion set or preserve typed certainty when the mutation result is ambiguous.
Solution: add provider-owned composable metrics deletion
Add exact low-level initiation, a copied-input non-replaying Client.Buckets operation, operation-last restart, typed Finish, and a synchronous wait over the same state machine. Extend pinned canonical-target verification, certainty normalization, signed native/lightweight sockets, bounded-response faults, and maintained documentation.
c12288e0DeleteBucketMetadataConfiguration is available only through the synchronous convenience path, so owner-driven callers cannot compose it, inspect typed admission certainty, or reuse the provider operation without a parallel wrapper.
Solution: add provider-owned composable metadata deletion
Add the exact Low_Level initiator and Client.Buckets limited constructor, operation-last restart, typed Finish, and typed synchronous wait over one non-replaying state machine. Gate exact operation binding, copied owner input, certainty normalization, bounded physical responses, native/lightweight sockets, model inventory, and API documentation.
b6552934DeleteBucketMetadataTableConfiguration remains on the last generic blocking bucket-configuration dispatcher. Owner-driven callers cannot compose it, inspect typed admission certainty, or reuse its operation without maintaining a parallel API path.
Solution: add provider-owned composable metadata-table deletion
Add the exact Low_Level initiator, limited constructor, operation-last restart, typed Finish, and synchronous wait over one nonreplaying state machine. Rewrite the convenience call through it, gate exact wire and lifecycle behavior, document the provider surface, and remove the now-dead generic dispatcher.
0cd84b09The pinned S3 surface can prepare the request, but callers cannot compose or strictly decode the complete lifecycle configuration graph. This leaves a common retention and cost-control read outside the provider-owned client.
Solution: add a complete provider-owned lifecycle read
Add the strict full-graph codec, exact low-level preparation and decoding, limited constructor, reusable initiation, typed Finish, and synchronous wait over one state machine. Gate all 36 modeled members, strict limits, headers, ownership, and native/lightweight execution, and record the client-only coverage boundary.
c361be0dThe current PutBucketLifecycleConfiguration operation has no exact typed low-level or provider-owned composable client, leaving callers without bounded serialization, request-checksum projection, or mutation certainty.
Solution: add the complete lifecycle configuration write client
Serialize the pinned request graph, add explicit low-level and provider-owned one-shot APIs, and qualify model identity, limits, signing, certainty, native/lightweight sockets, documentation, and coverage.
735fb607The current GetBucketNotificationConfiguration and
PutBucketNotificationConfiguration operations remain partial in the S3 ledger.
Callers cannot preserve the full destination and filter graph or use the
provider-owned limited constructor, reusable initiation, typed Finish, and
same-state-machine synchronous forms with explicit mutation certainty.
Solution: Add strict current notification GET and PUT clients
Add the bounded presence-preserving REST/XML codec, exact signed low-level
preparation and execution, provider-owned GET and non-replaying PUT state
machines, typed certainty mapping, deterministic model and socket gates, and
documented coverage. Keep the deprecated notification operations excluded.
735fb607 → updated to 5876862cindex fc0cd1dc1 source commit
5876862cCallers can prepare only part of the operation and cannot receive the pinned
replication graph through the provider-owned synchronous and composable state
machine. The coverage corpus therefore cannot qualify the current modeled
response, strict bounds, or prepared-operation admission boundary.
Solution: add the bounded provider-owned replication read
Add a strict presence-preserving replication XML codec, exact low-level
preparation and decoding, and synchronous, limited-constructor, reusable, and
typed Finish forms in Client.Buckets. Gate the complete pinned structural
graph, signed native/lightweight sockets, normalization, bounds, and coverage,
and document the prose-only cross-field policy boundary.
5876862c → updated to 0673cdadindex eecf5c641 source commit
0673cdadClients can read bucket replication configuration but cannot replace the same complete graph through the provider-owned composable boundary. Callers would otherwise have to assemble signing, one-shot body ownership, response bounds, and mutation certainty themselves, risking replay or an unsound definite-outcome classification.
Solution: Add nonreplaying provider-owned replication replacement
Serialize the reviewed presence-sensitive replication graph under caller limits, prepare and sign the exact modeled request, and expose low-level, limited-constructor, operation-last, typed Finish, and synchronous forms over one state machine. Add deterministic model/XML/certainty coverage, native and lightweight signed socket evidence, maintained documentation, and coverage bookkeeping.
0673cdad → updated to aff992c0index d1470f675 source commits
ded98183This repository is pinned to an older flyology-ada/agents revision, so its generated instructions and installed skills omit the current shared assurance workflows.
Solution: Update the shared agents dependency
Advance the lockfile to agents revision aa7c026, reinstall the package graph, and regenerate the Codex instructions.
c268566aUpdate shared agents to aa7c026
4da54070This consumer remains pinned to the prior shared agents revision, whose TLA+ workflow includes repository architecture constraints unrelated to using the skill.
Solution: Update the shared agents lock
Advance flyology-ada/agents to revision 6a97e7a and record the refined agent-focused TLA+ skill deployment hashes.
1ec12695Update shared agents to 6a97e7a
aff992c0Each new S3 operation manually repeats pinned-model parsing, coverage bookkeeping, verifier registration, corpus ordering, and documentation updates. The duplicated inventories have already drifted and make completing the remaining 116-operation surface slower and easier to misclassify.
Solution: establish a reviewed operation and evidence registry
Add a human-owned TOML inventory for all 116 pinned operations, a shared uv-driven model/audit/scaffold/qualify tool, generated coverage counts, test registration, and documentation, plus layer-specific executable-evidence checks and negative oracles. Preserve every existing coverage state and corpus order, use GetBucketReplication as the reviewed equivalence canary, and leave legacy Ada implementations unchanged for incremental family migration.
aff992c0 → updated to ace67878index 77fa38222 source commits
06c6331aProvider operations repeat response buffering, singleton-header validation, hidden HTTP child drain, cancellation, restart cleanup, and typed terminal storage. This slows operation coverage and lets semantically equivalent read paths drift.
Solution: Share the owner-driven bounded REST/XML read family
Add an implementation generic for invariant transport and ownership mechanics, then migrate GetBucketReplication as the retrospective canary. Keep provider-owned public signatures, request preparation, decoding, classification, and synchronous-over-composable behavior unchanged, and record executable shared-family provenance in the operation registry.
ace67878The root test gate invokes the generated S3 operation registry checks through uv, but the hosted test jobs do not install uv. Both Linux and macOS therefore fail after a successful Ada build, leaving the automation foundation unqualified.
Solution: Install and enforce the reviewed uv toolchain in CI
Install exact uv 0.11.28 through immutable setup-uv v9.0.0 in the integrity and test jobs. Extend workflow policy and its negative oracle so both the action and tool version remain pinned.
ace67878 → updated to f1a2a7b7index b8287ca41 source commit
f1a2a7b7Each bounded REST/XML operation manually duplicates exact HTTP targets, signed headers, malformed XML fixtures, socket sequencing, and test registration. Those copies can drift from the pinned Botocore model and make each operation expensive to qualify.
Solution: Generate data-driven qualification from reviewed decisions
Derive signed request plans and routine negative XML cases from the pinned model, run them through a shared uv loopback driver and small typed Ada adapter, and register executable evidence from the operation manifest. GetBucketReplication is the retrospective 23-case equivalence canary, while the prospective metrics query oracle verifies reusable canonical query generation.
f1a2a7b7 → updated to f65afbf2index a6c274401 source commit
f65afbf2The shared bounded REST/XML state machine and operation registry establish reusable transport and model evidence, but the first new operation must prove that reviewed API decisions, generated negative XML, signed requests, coverage registration, and documentation can advance without restoring per-operation shell bookkeeping. GetBucketMetricsConfiguration remains partial and invisible to callers until that path is exercised end to end.
Solution: qualify GetBucketMetricsConfiguration through the shared family
Add the strict presence-preserving metrics codec and provider-owned low-level, composable, restart, Finish, and synchronous forms over the shared state machine. Record only reviewed semantics and fixtures in the registry, derive the model-owned request and 17 malformed XML cases, run them through a small typed socket adapter, and make the independent coverage oracle discover test trees instead of a maintained filename list. Preserve explicit caller limits and the model's lack of an inferred filter-cardinality or identifier-content constraint.
f65afbf2 → updated to 4e5af762index 51207f331 source commit
4e5af762GetBucketAnalyticsConfiguration is tracked by the pinned S3 model but has no
typed provider API or executable client evidence. Callers cannot perform one
bounded named analytics read through the shared owner-driven state machine,
and the operation registry therefore retains partial client coverage.
Solution: qualify analytics reads through the shared REST/XML family
Add a strict model-aligned analytics codec, exact low-level preparation and
decoding, and provider-owned limited, restartable, typed Finish, and
synchronous forms. Register reviewed signed cases and model-derived negative
XML evidence, regenerate the coverage artifacts, and document the resulting
client-only compatibility boundary.
4e5af762 → updated to 7cbcb44eindex 826970c61 source commit
7cbcb44eGetBucketIntelligentTieringConfiguration is tracked by the pinned S3 model
but has no typed provider API or executable client evidence. Callers cannot
perform a bounded named configuration read through the shared owner-driven
state machine, and registry coverage remains partial.
Solution: qualify Intelligent-Tiering reads through the shared REST/XML family
Add a strict model-aligned codec, exact low-level preparation and decoding,
and provider-owned limited, restartable, typed Finish, and synchronous forms.
Register reviewed signed cases and model-derived negatives, regenerate
coverage artifacts, and document client-only compatibility.
7cbcb44e → updated to 9fb63d01index 2fe4325e1 source commit
9fb63d01Callers cannot read named S3 inventory configurations through the provider-
centric synchronous and composable APIs. The coverage registry therefore keeps
the operation partial even though it belongs to the established bounded
REST/XML read family.
Solution: add the typed inventory read through the shared family
Add the strict complete inventory codec, exact low-level preparation and
decoding, provider-owned constructor/restart/Finish/synchronous forms, and
model-derived negative and signed-socket qualification. Record the reviewed
model decisions and executable evidence without claiming backend, server, or
external-provider support.
9fb63d01 → updated to 10d46346index 400ea6431 source commit
10d46346The current development line leaves bucket logging as a partial generated client with no provider-owned composable operation or executable corpus. Its modeled payload also uses a wire-root alias, a nonflattened grant list, and required namespaced attributes that the operation verifier cannot yet qualify without manual negative-case work.
Solution: Add the strict logging client and extend model-driven qualification
Add the provider-centric low-level, composable, and synchronous Get_Logging forms over the shared bounded REST/XML read state machine. Decode the complete presence-preserving logging graph, disabled state, exact grantee attributes and enum domains, and modeled errors without adding defaults or replay policy. Extend the uv tool to verify structurally equivalent payload aliases, namespaced attributes, and nonflattened list members, then generate and execute the signed and negative XML evidence and update the coverage and architecture records.
10d46346 → updated to f5618821index d2404a6d1 source commit
f5618821The public client and signed corpus leave bucket website configuration unreadable, and the model verifier rejects the operation's documented payload alias even though its wire structure matches the reviewed WebsiteConfiguration shape. Repeating another handwritten bounded REST/XML read would also preserve the rollout bottleneck.
Solution: Add a strict provider-owned website read and model-driven evidence
Add the presence-preserving Website configuration codec, low-level and provider-owned composable/synchronous calls, generated negative XML and signed-socket qualification, and registry coverage. Verify payload aliases structurally while ignoring documentation-only differences and retaining all wire traits, so the shared bounded read family can qualify the operation without semantic drift.
f5618821 → updated to d0fd10d7index fdc77edc1 source commit
d0fd10d7The remaining bucket-configuration list operations share strict REST/XML page mechanics, request continuation projection, bounded response ownership, and signed qualification structure. Reimplementing those mechanics operation by operation slows coverage work and risks semantic or evidence drift.
Solution: Add a shared strict page family and qualify Metrics as its canary
Add a private paginated REST/XML envelope generic, reuse the reviewed metrics item codec, and expose provider-owned one-page synchronous and composable ListBucketMetricsConfigurations calls over the existing bounded read state machine. Record reviewed manifest decisions and generate the model-derived negative XML, signed socket, coverage, registration, and documentation evidence, while keeping continuation caller-driven and public policy unchanged.
d0fd10d7 → updated to f28a3fb6index b79170c41 source commit
f28a3fb6ListBucketAnalyticsConfigurations is still represented only by model inventory, so callers lack a qualified one-page provider API and each future implementation risks repeating page-envelope, ownership, and evidence mechanics.
Solution: Qualify Analytics through the paginated REST/XML family
Reuse the reviewed analytics item decoder inside the shared strict page envelope, add exact low-level and provider-owned synchronous/composable calls over the existing bounded state machine, and generate the model-derived negative, signed-socket, coverage, registration, and documentation evidence from reviewed manifest decisions.
0.1.0-devSupervised S3 server and management console for Flyology Object Storage
6ec20483 → updated to 735fb607index a47fabdb25 source commits
94c0ee9cThe exact lifecycle deletion remains limited to blocking convenience and prepared execution. Callers cannot own it in a completion set, preserve typed admission certainty, or reuse its state without a parallel wrapper.
Solution: add the provider-owned lifecycle deletion operation
Add the exact low-level initiator and colocated Client.Buckets constructor, operation-last initiation, typed Finish, and synchronous wait over one non-replaying state machine. Gate certainty, prepared-operation identity, physical headers, response bounds, restart, native/lightweight execution, and document the provider inventory.
571522b3The existing convenience client executes synchronously through its own path, so callers cannot compose the exact signed policy-status read with other provider operations or reuse its operation state safely.
Solution: colocate a bounded composable policy-status operation
Add the exact low-level initiator, provider-owned limited constructor, operation-last restart, typed Finish, and typed synchronous wait. Drive the convenience overload through the same bounded state machine, reject malformed singleton headers and over-limit bodies, and qualify the result mapping and native/lightweight socket behavior.
1c0455b9Requester-payment reads are available only through the blocking executor, so structured callers cannot own their deadline, cancellation, operation reuse, typed HTTP outcome, and bounded response lifecycle alongside other work.
Solution: Colocate a bounded composable requester-payment read
Add exact prepared-request admission, provider-owned limited construction and operation-last restart, typed Finish and synchronous wait, strict header and response bounds, full normalization and loopback corpora, and maintained API documentation. The established convenience call now waits on the same state machine without adding retry or billing-policy interpretation.
6befc49aThe synchronous requester-payment mutation owns no reusable operation, exposes no admission certainty, and cannot participate in a caller-owned completion set.
Solution: add a non-replaying provider-owned requester-payment operation
Add the exact low-level initiator, limited constructor, operation-last restart, typed Finish and synchronous wait over one state machine. Own the signed payer bytes through terminal drain, preserve strict response bounds and headers, classify ambiguous outcomes without replay, and gate the contract with certainty, socket, model, documentation and repository checks.
4664482fCallers can only execute PutBucketAbac synchronously, so owner-driven state machines cannot retain typed publication certainty or reconcile ambiguous outcomes without a parallel adapter.
Solution: add a provider-owned non-replaying ABAC operation
Expose exact low-level initiation and Client.Buckets limited-constructor, operation-last, typed Finish, and synchronous-wait forms over one state machine. Preserve the serialized status as a one-shot owned source, normalize admission certainty, extend exact-operation and socket corpora, and document the provider inventory and reconciliation contract.
cc04da27Callers can only execute the acceleration mutation synchronously, so owner-driven workflows cannot preserve typed admission certainty or reconcile ambiguous publication without a separate adapter.
Solution: add the provider-owned acceleration operation
Expose the exact one-shot low-level initiator and Client.Buckets limited-constructor, operation-last, typed Finish, and synchronous-wait forms over one state machine. Preserve the model's no-Content-MD5 rule, normalize mutation certainty, extend exact-operation and socket corpora, and document read reconciliation and the updated provider inventory.
062f8119Callers can read the optional ABAC state only through a blocking convenience call, so a possibly admitted PutBucketAbac cannot compose its read-only reconciliation with other owner-driven operations. The low-level prepared-request boundary also has no exact reusable initiator for this operation.
Solution: add one provider-owned bounded GetBucketAbac state machine
Add the exact Low_Level initiator, limited constructor, operation-last reusable form, typed Finish, and typed synchronous wait in Client.Buckets. Drive the convenience call through that same state machine, preserve the caller-selected XML bound and inherited defaults, and qualify exact operation binding, restart, response normalization, singleton headers, limits, documentation, and ownership without adding retry or helper tasks.
59ea351bCallers that need to reconcile PutBucketAccelerateConfiguration outcomes can only use the blocking convenience read. The exact prepared request has no reusable initiator, so owner-driven workflows cannot compose the bounded response without introducing a parallel state machine.
Solution: colocate a bounded acceleration read with the Buckets provider
Add the exact Low_Level initiator, limited constructor, operation-last restart, typed Finish, and typed synchronous wait. Drive the existing convenience overload through the same provider-owned state machine, enforce caller-selected response bounds and singleton headers, and qualify request binding, restart, normalization, malformed headers, and overflow without retries or helper tasks.
45564e81GetBucketAcl has a strict typed low-level synchronous client and an authenticated server route, but no provider-owned limited operation. Callers cannot coordinate ACL reads with completion-set workflows without introducing a parallel execution path.
Solution: colocate a bounded ACL read with the Buckets provider
Add the exact Low_Level initiator, limited constructor, operation-last restart, typed Finish, and typed synchronous wait. Retain the signed request and bounded response through terminal drain, preserve physical singleton headers and admission diagnostics, and qualify exact binding, restart, normalization, and response limits without retries or helper tasks.
6b4e9836GetObjectAcl has a strict typed synchronous client and authenticated server route, but it lacks the provider-owned limited operation needed by caller-driven completion sets. Callers otherwise need a second API shape and cannot reuse the exact signed request, bounded response, admission certainty, or failure mapping in a larger state machine.
Solution: colocate a bounded ACL read with the Objects provider
Add the exact Low_Level sink initiator and Client.Objects limited constructor, operation-last restart, typed Finish, and synchronous wait over one owner-driven state machine. Preserve exact version, requester-pays, expected-owner, singleton-header, caller-limit, cancellation, drain, and no-retry behavior, and gate the surface with preparation, restart, failure-normalization, socket, documentation, and full deterministic corpora.
22a5e68dGetBucketMetadataTableConfiguration has strict preparation, decoding, and synchronous execution, but no provider-owned limited operation. Callers cannot compose the exact signed request, bounded response snapshot, admission certainty, or typed failure mapping into a larger completion-set state machine.
Solution: colocate the bounded read with the Buckets provider
Add the exact Low_Level sink initiator and Client.Buckets limited constructor, operation-last restart, typed Finish, and synchronous wait over one owner-driven state machine. Preserve the opaque provider status, expected-owner projection, singleton diagnostics, caller XML limits, cancellation, drain, and no-retry behavior, and gate the surface with exact preparation, restart, normalization, socket, documentation, and full deterministic corpora.
0d0dbbacCallers can prepare and synchronously execute CreateBucketMetadataTableConfiguration, but they cannot initiate, restart, or consume it from a provider-owned completion set. The replayable prepared body also obscures the mutation certainty and read-before-retry contract after possible admission.
Solution: add one-shot provider-owned metadata-table creation
Move the prepared payload onto the existing non-rewindable owned-source path, add exact low-level admission gating, and colocate the limited constructor, operation-last restart, typed Finish, and typed synchronous wait in Client.Buckets. Preserve signed MD5/checksum and owner projection, expose conservative mutation certainty, require read-only reconciliation for unknown outcomes, and gate ownership, bounds, restart, cross-operation rejection, and the complete certainty matrix.
c457fcd8DeleteObjectAnnotation is limited to a synchronous low-level call, so owner-driven object workflows cannot schedule it beside other provider operations or retain typed admission certainty through terminal drain.
Solution: colocate nonreplaying annotation deletion with objects
Add an exact low-level initiator and provider-owned constructor, operation-last restart, typed Finish, and synchronous wait over one state machine. Copy all request identity, preserve one-shot mutation semantics, normalize publication certainty conservatively, and gate the API with exhaustive mapping and signed socket coverage.
f1cd6026DeleteBucketReplication is available only through synchronous convenience calls, so owner-driven callers cannot retain typed admission certainty, reuse an established operation, or compose it with sibling work.
Solution: colocate composable replication deletion with the bucket provider
Add the exact low-level initiator and provider-owned limited operation, operation-last initiation, typed Finish, and typed synchronous wait. Drive the convenience form through the same non-replaying state machine and gate its certainty, ownership, bounds, wire behavior, and documentation exhaustively.
068b0a9aDeleteBucketWebsite is available only through synchronous convenience calls, so owner-driven callers cannot retain typed admission certainty, reuse an established operation, or compose it with sibling work.
Solution: colocate composable website deletion with the bucket provider
Add the exact low-level initiator and provider-owned limited operation, operation-last initiation, typed Finish, and typed synchronous wait. Drive the convenience form through the same non-replaying state machine and gate its certainty, ownership, bounds, wire behavior, and documentation exhaustively.
7fd0ffc1DeleteBucketInventoryConfiguration is available only through a blocking convenience call, so callers cannot schedule it with other owner-driven operations or retain typed publication certainty after possible admission.
Solution: add a provider-owned composable inventory deletion
Add the exact low-level initiator, limited constructor, operation-last restart, typed Finish and synchronous wait over one non-replaying state machine. Extend identifier-copy, certainty, response-bound, signed socket, verifier, API documentation and compatibility evidence.
d71a924dDirectory-bucket callers must use a blocking scalar wrapper, cannot schedule the exchange beside other operations, and cannot inspect typed transport failure or admission certainty. The limited zeroizing credentials result also cannot safely live inside reusable operation storage.
Solution: add a provider-owned composable CreateSession
Retain the signed request, physically validated response metadata, and bounded raw response until typed Finish constructs credentials exactly once. Add same-name limited and operation-last forms, make both synchronous forms wait on the same state machine, and qualify cancellation, restart, request copying, response bounds, and malformed-response normalization.
55460971DeleteBucketIntelligentTieringConfiguration is available only through the synchronous bucket client, so callers cannot place it in a shared completion set or inspect typed admission certainty after an ambiguous mutation result.
Solution: add provider-owned composable intelligent-tiering deletion
Add exact low-level initiation, a non-replaying Client.Buckets operation with operation-last restart and typed Finish, and route the synchronous forms through the same state machine. Qualify copied inputs, certainty normalization, response bounds, malformed headers, documentation, and the pinned operation inventory.
4bf62837DeleteBucketAnalyticsConfiguration is available only through the synchronous bucket client, so callers cannot share a completion set or retain typed certainty when deletion may have reached the service.
Solution: add provider-owned composable analytics deletion
Add an exact low-level initiator, a non-replaying Client.Buckets operation with copied request inputs, operation-last restart, typed Finish, and a synchronous wait over the same state machine. Extend the pinned verifier, certainty matrix, signed native/lightweight socket corpus, and maintained API documentation.
d50be24cDeleteBucketMetricsConfiguration is available only through the synchronous bucket client, so callers cannot share a completion set or preserve typed certainty when the mutation result is ambiguous.
Solution: add provider-owned composable metrics deletion
Add exact low-level initiation, a copied-input non-replaying Client.Buckets operation, operation-last restart, typed Finish, and a synchronous wait over the same state machine. Extend pinned canonical-target verification, certainty normalization, signed native/lightweight sockets, bounded-response faults, and maintained documentation.
c12288e0DeleteBucketMetadataConfiguration is available only through the synchronous convenience path, so owner-driven callers cannot compose it, inspect typed admission certainty, or reuse the provider operation without a parallel wrapper.
Solution: add provider-owned composable metadata deletion
Add the exact Low_Level initiator and Client.Buckets limited constructor, operation-last restart, typed Finish, and typed synchronous wait over one non-replaying state machine. Gate exact operation binding, copied owner input, certainty normalization, bounded physical responses, native/lightweight sockets, model inventory, and API documentation.
b6552934DeleteBucketMetadataTableConfiguration remains on the last generic blocking bucket-configuration dispatcher. Owner-driven callers cannot compose it, inspect typed admission certainty, or reuse its operation without maintaining a parallel API path.
Solution: add provider-owned composable metadata-table deletion
Add the exact Low_Level initiator, limited constructor, operation-last restart, typed Finish, and synchronous wait over one nonreplaying state machine. Rewrite the convenience call through it, gate exact wire and lifecycle behavior, document the provider surface, and remove the now-dead generic dispatcher.
0cd84b09The pinned S3 surface can prepare the request, but callers cannot compose or strictly decode the complete lifecycle configuration graph. This leaves a common retention and cost-control read outside the provider-owned client.
Solution: add a complete provider-owned lifecycle read
Add the strict full-graph codec, exact low-level preparation and decoding, limited constructor, reusable initiation, typed Finish, and synchronous wait over one state machine. Gate all 36 modeled members, strict limits, headers, ownership, and native/lightweight execution, and record the client-only coverage boundary.
c361be0dThe current PutBucketLifecycleConfiguration operation has no exact typed low-level or provider-owned composable client, leaving callers without bounded serialization, request-checksum projection, or mutation certainty.
Solution: add the complete lifecycle configuration write client
Serialize the pinned request graph, add explicit low-level and provider-owned one-shot APIs, and qualify model identity, limits, signing, certainty, native/lightweight sockets, documentation, and coverage.
735fb607The current GetBucketNotificationConfiguration and
PutBucketNotificationConfiguration operations remain partial in the S3 ledger.
Callers cannot preserve the full destination and filter graph or use the
provider-owned limited constructor, reusable initiation, typed Finish, and
same-state-machine synchronous forms with explicit mutation certainty.
Solution: Add strict current notification GET and PUT clients
Add the bounded presence-preserving REST/XML codec, exact signed low-level
preparation and execution, provider-owned GET and non-replaying PUT state
machines, typed certainty mapping, deterministic model and socket gates, and
documented coverage. Keep the deprecated notification operations excluded.
735fb607 → updated to 5876862cindex fc0cd1dc1 source commit
5876862cCallers can prepare only part of the operation and cannot receive the pinned
replication graph through the provider-owned synchronous and composable state
machine. The coverage corpus therefore cannot qualify the current modeled
response, strict bounds, or prepared-operation admission boundary.
Solution: add the bounded provider-owned replication read
Add a strict presence-preserving replication XML codec, exact low-level
preparation and decoding, and synchronous, limited-constructor, reusable, and
typed Finish forms in Client.Buckets. Gate the complete pinned structural
graph, signed native/lightweight sockets, normalization, bounds, and coverage,
and document the prose-only cross-field policy boundary.
5876862c → updated to 0673cdadindex eecf5c641 source commit
0673cdadClients can read bucket replication configuration but cannot replace the same complete graph through the provider-owned composable boundary. Callers would otherwise have to assemble signing, one-shot body ownership, response bounds, and mutation certainty themselves, risking replay or an unsound definite-outcome classification.
Solution: Add nonreplaying provider-owned replication replacement
Serialize the reviewed presence-sensitive replication graph under caller limits, prepare and sign the exact modeled request, and expose low-level, limited-constructor, operation-last, typed Finish, and synchronous forms over one state machine. Add deterministic model/XML/certainty coverage, native and lightweight signed socket evidence, maintained documentation, and coverage bookkeeping.
0673cdad → updated to aff992c0index d1470f675 source commits
ded98183This repository is pinned to an older flyology-ada/agents revision, so its generated instructions and installed skills omit the current shared assurance workflows.
Solution: Update the shared agents dependency
Advance the lockfile to agents revision aa7c026, reinstall the package graph, and regenerate the Codex instructions.
c268566aUpdate shared agents to aa7c026
4da54070This consumer remains pinned to the prior shared agents revision, whose TLA+ workflow includes repository architecture constraints unrelated to using the skill.
Solution: Update the shared agents lock
Advance flyology-ada/agents to revision 6a97e7a and record the refined agent-focused TLA+ skill deployment hashes.
1ec12695Update shared agents to 6a97e7a
aff992c0Each new S3 operation manually repeats pinned-model parsing, coverage bookkeeping, verifier registration, corpus ordering, and documentation updates. The duplicated inventories have already drifted and make completing the remaining 116-operation surface slower and easier to misclassify.
Solution: establish a reviewed operation and evidence registry
Add a human-owned TOML inventory for all 116 pinned operations, a shared uv-driven model/audit/scaffold/qualify tool, generated coverage counts, test registration, and documentation, plus layer-specific executable-evidence checks and negative oracles. Preserve every existing coverage state and corpus order, use GetBucketReplication as the reviewed equivalence canary, and leave legacy Ada implementations unchanged for incremental family migration.
aff992c0 → updated to ace67878index 77fa38222 source commits
06c6331aProvider operations repeat response buffering, singleton-header validation, hidden HTTP child drain, cancellation, restart cleanup, and typed terminal storage. This slows operation coverage and lets semantically equivalent read paths drift.
Solution: Share the owner-driven bounded REST/XML read family
Add an implementation generic for invariant transport and ownership mechanics, then migrate GetBucketReplication as the retrospective canary. Keep provider-owned public signatures, request preparation, decoding, classification, and synchronous-over-composable behavior unchanged, and record executable shared-family provenance in the operation registry.
ace67878The root test gate invokes the generated S3 operation registry checks through uv, but the hosted test jobs do not install uv. Both Linux and macOS therefore fail after a successful Ada build, leaving the automation foundation unqualified.
Solution: Install and enforce the reviewed uv toolchain in CI
Install exact uv 0.11.28 through immutable setup-uv v9.0.0 in the integrity and test jobs. Extend workflow policy and its negative oracle so both the action and tool version remain pinned.
ace67878 → updated to f1a2a7b7index b8287ca41 source commit
f1a2a7b7Each bounded REST/XML operation manually duplicates exact HTTP targets, signed headers, malformed XML fixtures, socket sequencing, and test registration. Those copies can drift from the pinned Botocore model and make each operation expensive to qualify.
Solution: Generate data-driven qualification from reviewed decisions
Derive signed request plans and routine negative XML cases from the pinned model, run them through a shared uv loopback driver and small typed Ada adapter, and register executable evidence from the operation manifest. GetBucketReplication is the retrospective 23-case equivalence canary, while the prospective metrics query oracle verifies reusable canonical query generation.
f1a2a7b7 → updated to f65afbf2index a6c274401 source commit
f65afbf2The shared bounded REST/XML state machine and operation registry establish reusable transport and model evidence, but the first new operation must prove that reviewed API decisions, generated negative XML, signed requests, coverage registration, and documentation can advance without restoring per-operation shell bookkeeping. GetBucketMetricsConfiguration remains partial and invisible to callers until that path is exercised end to end.
Solution: qualify GetBucketMetricsConfiguration through the shared family
Add the strict presence-preserving metrics codec and provider-owned low-level, composable, restart, Finish, and synchronous forms over the shared state machine. Record only reviewed semantics and fixtures in the registry, derive the model-owned request and 17 malformed XML cases, run them through a small typed socket adapter, and make the independent coverage oracle discover test trees instead of a maintained filename list. Preserve explicit caller limits and the model's lack of an inferred filter-cardinality or identifier-content constraint.
f65afbf2 → updated to 4e5af762index 51207f331 source commit
4e5af762GetBucketAnalyticsConfiguration is tracked by the pinned S3 model but has no
typed provider API or executable client evidence. Callers cannot perform one
bounded named analytics read through the shared owner-driven state machine,
and the operation registry therefore retains partial client coverage.
Solution: qualify analytics reads through the shared REST/XML family
Add a strict model-aligned analytics codec, exact low-level preparation and
decoding, and provider-owned limited, restartable, typed Finish, and
synchronous forms. Register reviewed signed cases and model-derived negative
XML evidence, regenerate the coverage artifacts, and document the resulting
client-only compatibility boundary.
4e5af762 → updated to 7cbcb44eindex 826970c61 source commit
7cbcb44eGetBucketIntelligentTieringConfiguration is tracked by the pinned S3 model
but has no typed provider API or executable client evidence. Callers cannot
perform a bounded named configuration read through the shared owner-driven
state machine, and registry coverage remains partial.
Solution: qualify Intelligent-Tiering reads through the shared REST/XML family
Add a strict model-aligned codec, exact low-level preparation and decoding,
and provider-owned limited, restartable, typed Finish, and synchronous forms.
Register reviewed signed cases and model-derived negatives, regenerate
coverage artifacts, and document client-only compatibility.
7cbcb44e → updated to 9fb63d01index 2fe4325e1 source commit
9fb63d01Callers cannot read named S3 inventory configurations through the provider-
centric synchronous and composable APIs. The coverage registry therefore keeps
the operation partial even though it belongs to the established bounded
REST/XML read family.
Solution: add the typed inventory read through the shared family
Add the strict complete inventory codec, exact low-level preparation and
decoding, provider-owned constructor/restart/Finish/synchronous forms, and
model-derived negative and signed-socket qualification. Record the reviewed
model decisions and executable evidence without claiming backend, server, or
external-provider support.
9fb63d01 → updated to 10d46346index 400ea6431 source commit
10d46346The current development line leaves bucket logging as a partial generated client with no provider-owned composable operation or executable corpus. Its modeled payload also uses a wire-root alias, a nonflattened grant list, and required namespaced attributes that the operation verifier cannot yet qualify without manual negative-case work.
Solution: Add the strict logging client and extend model-driven qualification
Add the provider-centric low-level, composable, and synchronous Get_Logging forms over the shared bounded REST/XML read state machine. Decode the complete presence-preserving logging graph, disabled state, exact grantee attributes and enum domains, and modeled errors without adding defaults or replay policy. Extend the uv tool to verify structurally equivalent payload aliases, namespaced attributes, and nonflattened list members, then generate and execute the signed and negative XML evidence and update the coverage and architecture records.
10d46346 → updated to f5618821index d2404a6d1 source commit
f5618821The public client and signed corpus leave bucket website configuration unreadable, and the model verifier rejects the operation's documented payload alias even though its wire structure matches the reviewed WebsiteConfiguration shape. Repeating another handwritten bounded REST/XML read would also preserve the rollout bottleneck.
Solution: Add a strict provider-owned website read and model-driven evidence
Add the presence-preserving Website configuration codec, low-level and provider-owned composable/synchronous calls, generated negative XML and signed-socket qualification, and registry coverage. Verify payload aliases structurally while ignoring documentation-only differences and retaining all wire traits, so the shared bounded read family can qualify the operation without semantic drift.
f5618821 → updated to d0fd10d7index fdc77edc1 source commit
d0fd10d7The remaining bucket-configuration list operations share strict REST/XML page mechanics, request continuation projection, bounded response ownership, and signed qualification structure. Reimplementing those mechanics operation by operation slows coverage work and risks semantic or evidence drift.
Solution: Add a shared strict page family and qualify Metrics as its canary
Add a private paginated REST/XML envelope generic, reuse the reviewed metrics item codec, and expose provider-owned one-page synchronous and composable ListBucketMetricsConfigurations calls over the existing bounded read state machine. Record reviewed manifest decisions and generate the model-derived negative XML, signed socket, coverage, registration, and documentation evidence, while keeping continuation caller-driven and public policy unchanged.
d0fd10d7 → updated to f28a3fb6index b79170c41 source commit
f28a3fb6ListBucketAnalyticsConfigurations is still represented only by model inventory, so callers lack a qualified one-page provider API and each future implementation risks repeating page-envelope, ownership, and evidence mechanics.
Solution: Qualify Analytics through the paginated REST/XML family
Reuse the reviewed analytics item decoder inside the shared strict page envelope, add exact low-level and provider-owned synchronous/composable calls over the existing bounded state machine, and generate the model-derived negative, signed-socket, coverage, registration, and documentation evidence from reviewed manifest decisions.
0.1.0-devSQLite catalog backend for Flyology Object Storage
6ec20483 → updated to 735fb607index a47fabdb25 source commits
94c0ee9cThe exact lifecycle deletion remains limited to blocking convenience and prepared execution. Callers cannot own it in a completion set, preserve typed admission certainty, or reuse its state without a parallel wrapper.
Solution: add the provider-owned lifecycle deletion operation
Add the exact low-level initiator and colocated Client.Buckets constructor, operation-last initiation, typed Finish, and synchronous wait over one non-replaying state machine. Gate certainty, prepared-operation identity, physical headers, response bounds, restart, native/lightweight execution, and document the provider inventory.
571522b3The existing convenience client executes synchronously through its own path, so callers cannot compose the exact signed policy-status read with other provider operations or reuse its operation state safely.
Solution: colocate a bounded composable policy-status operation
Add the exact low-level initiator, provider-owned limited constructor, operation-last restart, typed Finish, and typed synchronous wait. Drive the convenience overload through the same bounded state machine, reject malformed singleton headers and over-limit bodies, and qualify the result mapping and native/lightweight socket behavior.
1c0455b9Requester-payment reads are available only through the blocking executor, so structured callers cannot own their deadline, cancellation, operation reuse, typed HTTP outcome, and bounded response lifecycle alongside other work.
Solution: Colocate a bounded composable requester-payment read
Add exact prepared-request admission, provider-owned limited construction and operation-last restart, typed Finish and synchronous wait, strict header and response bounds, full normalization and loopback corpora, and maintained API documentation. The established convenience call now waits on the same state machine without adding retry or billing-policy interpretation.
6befc49aThe synchronous requester-payment mutation owns no reusable operation, exposes no admission certainty, and cannot participate in a caller-owned completion set.
Solution: add a non-replaying provider-owned requester-payment operation
Add the exact low-level initiator, limited constructor, operation-last restart, typed Finish and synchronous wait over one state machine. Own the signed payer bytes through terminal drain, preserve strict response bounds and headers, classify ambiguous outcomes without replay, and gate the contract with certainty, socket, model, documentation and repository checks.
4664482fCallers can only execute PutBucketAbac synchronously, so owner-driven state machines cannot retain typed publication certainty or reconcile ambiguous outcomes without a parallel adapter.
Solution: add a provider-owned non-replaying ABAC operation
Expose exact low-level initiation and Client.Buckets limited-constructor, operation-last, typed Finish, and synchronous-wait forms over one state machine. Preserve the serialized status as a one-shot owned source, normalize admission certainty, extend exact-operation and socket corpora, and document the provider inventory and reconciliation contract.
cc04da27Callers can only execute the acceleration mutation synchronously, so owner-driven workflows cannot preserve typed admission certainty or reconcile ambiguous publication without a separate adapter.
Solution: add the provider-owned acceleration operation
Expose the exact one-shot low-level initiator and Client.Buckets limited-constructor, operation-last, typed Finish, and synchronous-wait forms over one state machine. Preserve the model's no-Content-MD5 rule, normalize mutation certainty, extend exact-operation and socket corpora, and document read reconciliation and the updated provider inventory.
062f8119Callers can read the optional ABAC state only through a blocking convenience call, so a possibly admitted PutBucketAbac cannot compose its read-only reconciliation with other owner-driven operations. The low-level prepared-request boundary also has no exact reusable initiator for this operation.
Solution: add one provider-owned bounded GetBucketAbac state machine
Add the exact Low_Level initiator, limited constructor, operation-last reusable form, typed Finish, and typed synchronous wait in Client.Buckets. Drive the convenience call through that same state machine, preserve the caller-selected XML bound and inherited defaults, and qualify exact operation binding, restart, response normalization, singleton headers, limits, documentation, and ownership without adding retry or helper tasks.
59ea351bCallers that need to reconcile PutBucketAccelerateConfiguration outcomes can only use the blocking convenience read. The exact prepared request has no reusable initiator, so owner-driven workflows cannot compose the bounded response without introducing a parallel state machine.
Solution: colocate a bounded acceleration read with the Buckets provider
Add the exact Low_Level initiator, limited constructor, operation-last restart, typed Finish, and typed synchronous wait. Drive the existing convenience overload through the same provider-owned state machine, enforce caller-selected response bounds and singleton headers, and qualify request binding, restart, normalization, malformed headers, and overflow without retries or helper tasks.
45564e81GetBucketAcl has a strict typed low-level synchronous client and an authenticated server route, but no provider-owned limited operation. Callers cannot coordinate ACL reads with completion-set workflows without introducing a parallel execution path.
Solution: colocate a bounded ACL read with the Buckets provider
Add the exact Low_Level initiator, limited constructor, operation-last restart, typed Finish, and typed synchronous wait. Retain the signed request and bounded response through terminal drain, preserve physical singleton headers and admission diagnostics, and qualify exact binding, restart, normalization, and response limits without retries or helper tasks.
6b4e9836GetObjectAcl has a strict typed synchronous client and authenticated server route, but it lacks the provider-owned limited operation needed by caller-driven completion sets. Callers otherwise need a second API shape and cannot reuse the exact signed request, bounded response, admission certainty, or failure mapping in a larger state machine.
Solution: colocate a bounded ACL read with the Objects provider
Add the exact Low_Level sink initiator and Client.Objects limited constructor, operation-last restart, typed Finish, and synchronous wait over one owner-driven state machine. Preserve exact version, requester-pays, expected-owner, singleton-header, caller-limit, cancellation, drain, and no-retry behavior, and gate the surface with preparation, restart, failure-normalization, socket, documentation, and full deterministic corpora.
22a5e68dGetBucketMetadataTableConfiguration has strict preparation, decoding, and synchronous execution, but no provider-owned limited operation. Callers cannot compose the exact signed request, bounded response snapshot, admission certainty, or typed failure mapping into a larger completion-set state machine.
Solution: colocate the bounded read with the Buckets provider
Add the exact Low_Level sink initiator and Client.Buckets limited constructor, operation-last restart, typed Finish, and synchronous wait over one owner-driven state machine. Preserve the opaque provider status, expected-owner projection, singleton diagnostics, caller XML limits, cancellation, drain, and no-retry behavior, and gate the surface with exact preparation, restart, normalization, socket, documentation, and full deterministic corpora.
0d0dbbacCallers can prepare and synchronously execute CreateBucketMetadataTableConfiguration, but they cannot initiate, restart, or consume it from a provider-owned completion set. The replayable prepared body also obscures the mutation certainty and read-before-retry contract after possible admission.
Solution: add one-shot provider-owned metadata-table creation
Move the prepared payload onto the existing non-rewindable owned-source path, add exact low-level admission gating, and colocate the limited constructor, operation-last restart, typed Finish, and typed synchronous wait in Client.Buckets. Preserve signed MD5/checksum and owner projection, expose conservative mutation certainty, require read-only reconciliation for unknown outcomes, and gate ownership, bounds, restart, cross-operation rejection, and the complete certainty matrix.
c457fcd8DeleteObjectAnnotation is limited to a synchronous low-level call, so owner-driven object workflows cannot schedule it beside other provider operations or retain typed admission certainty through terminal drain.
Solution: colocate nonreplaying annotation deletion with objects
Add an exact low-level initiator and provider-owned constructor, operation-last restart, typed Finish, and synchronous wait over one state machine. Copy all request identity, preserve one-shot mutation semantics, normalize publication certainty conservatively, and gate the API with exhaustive mapping and signed socket coverage.
f1cd6026DeleteBucketReplication is available only through synchronous convenience calls, so owner-driven callers cannot retain typed admission certainty, reuse an established operation, or compose it with sibling work.
Solution: colocate composable replication deletion with the bucket provider
Add the exact low-level initiator and provider-owned limited operation, operation-last initiation, typed Finish, and typed synchronous wait. Drive the convenience form through the same non-replaying state machine and gate its certainty, ownership, bounds, wire behavior, and documentation exhaustively.
068b0a9aDeleteBucketWebsite is available only through synchronous convenience calls, so owner-driven callers cannot retain typed admission certainty, reuse an established operation, or compose it with sibling work.
Solution: colocate composable website deletion with the bucket provider
Add the exact low-level initiator and provider-owned limited operation, operation-last initiation, typed Finish, and typed synchronous wait. Drive the convenience form through the same non-replaying state machine and gate its certainty, ownership, bounds, wire behavior, and documentation exhaustively.
7fd0ffc1DeleteBucketInventoryConfiguration is available only through a blocking convenience call, so callers cannot schedule it with other owner-driven operations or retain typed publication certainty after possible admission.
Solution: add a provider-owned composable inventory deletion
Add the exact low-level initiator, limited constructor, operation-last restart, typed Finish and synchronous wait over one non-replaying state machine. Extend identifier-copy, certainty, response-bound, signed socket, verifier, API documentation and compatibility evidence.
d71a924dDirectory-bucket callers must use a blocking scalar wrapper, cannot schedule the exchange beside other operations, and cannot inspect typed transport failure or admission certainty. The limited zeroizing credentials result also cannot safely live inside reusable operation storage.
Solution: add a provider-owned composable CreateSession
Retain the signed request, physically validated response metadata, and bounded raw response until typed Finish constructs credentials exactly once. Add same-name limited and operation-last forms, make both synchronous forms wait on the same state machine, and qualify cancellation, restart, request copying, response bounds, and malformed-response normalization.
55460971DeleteBucketIntelligentTieringConfiguration is available only through the synchronous bucket client, so callers cannot place it in a shared completion set or inspect typed admission certainty after an ambiguous mutation result.
Solution: add provider-owned composable intelligent-tiering deletion
Add exact low-level initiation, a non-replaying Client.Buckets operation with operation-last restart and typed Finish, and route the synchronous forms through the same state machine. Qualify copied inputs, certainty normalization, response bounds, malformed headers, documentation, and the pinned operation inventory.
4bf62837DeleteBucketAnalyticsConfiguration is available only through the synchronous bucket client, so callers cannot share a completion set or retain typed certainty when deletion may have reached the service.
Solution: add provider-owned composable analytics deletion
Add an exact low-level initiator, a non-replaying Client.Buckets operation with copied request inputs, operation-last restart, typed Finish, and a synchronous wait over the same state machine. Extend the pinned verifier, certainty matrix, signed native/lightweight socket corpus, and maintained API documentation.
d50be24cDeleteBucketMetricsConfiguration is available only through the synchronous bucket client, so callers cannot share a completion set or preserve typed certainty when the mutation result is ambiguous.
Solution: add provider-owned composable metrics deletion
Add exact low-level initiation, a copied-input non-replaying Client.Buckets operation, operation-last restart, typed Finish, and a synchronous wait over the same state machine. Extend pinned canonical-target verification, certainty normalization, signed native/lightweight sockets, bounded-response faults, and maintained documentation.
c12288e0DeleteBucketMetadataConfiguration is available only through the synchronous convenience path, so owner-driven callers cannot compose it, inspect typed admission certainty, or reuse the provider operation without a parallel wrapper.
Solution: add provider-owned composable metadata deletion
Add the exact Low_Level initiator and Client.Buckets limited constructor, operation-last restart, typed Finish, and typed synchronous wait over one non-replaying state machine. Gate exact operation binding, copied owner input, certainty normalization, bounded physical responses, native/lightweight sockets, model inventory, and API documentation.
b6552934DeleteBucketMetadataTableConfiguration remains on the last generic blocking bucket-configuration dispatcher. Owner-driven callers cannot compose it, inspect typed admission certainty, or reuse its operation without maintaining a parallel API path.
Solution: add provider-owned composable metadata-table deletion
Add the exact Low_Level initiator, limited constructor, operation-last restart, typed Finish, and synchronous wait over one nonreplaying state machine. Rewrite the convenience call through it, gate exact wire and lifecycle behavior, document the provider surface, and remove the now-dead generic dispatcher.
0cd84b09The pinned S3 surface can prepare the request, but callers cannot compose or strictly decode the complete lifecycle configuration graph. This leaves a common retention and cost-control read outside the provider-owned client.
Solution: add a complete provider-owned lifecycle read
Add the strict full-graph codec, exact low-level preparation and decoding, limited constructor, reusable initiation, typed Finish, and synchronous wait over one state machine. Gate all 36 modeled members, strict limits, headers, ownership, and native/lightweight execution, and record the client-only coverage boundary.
c361be0dThe current PutBucketLifecycleConfiguration operation has no exact typed low-level or provider-owned composable client, leaving callers without bounded serialization, request-checksum projection, or mutation certainty.
Solution: add the complete lifecycle configuration write client
Serialize the pinned request graph, add explicit low-level and provider-owned one-shot APIs, and qualify model identity, limits, signing, certainty, native/lightweight sockets, documentation, and coverage.
735fb607The current GetBucketNotificationConfiguration and
PutBucketNotificationConfiguration operations remain partial in the S3 ledger.
Callers cannot preserve the full destination and filter graph or use the
provider-owned limited constructor, reusable initiation, typed Finish, and
same-state-machine synchronous forms with explicit mutation certainty.
Solution: Add strict current notification GET and PUT clients
Add the bounded presence-preserving REST/XML codec, exact signed low-level
preparation and execution, provider-owned GET and non-replaying PUT state
machines, typed certainty mapping, deterministic model and socket gates, and
documented coverage. Keep the deprecated notification operations excluded.
735fb607 → updated to 5876862cindex fc0cd1dc1 source commit
5876862cCallers can prepare only part of the operation and cannot receive the pinned
replication graph through the provider-owned synchronous and composable state
machine. The coverage corpus therefore cannot qualify the current modeled
response, strict bounds, or prepared-operation admission boundary.
Solution: add the bounded provider-owned replication read
Add a strict presence-preserving replication XML codec, exact low-level
preparation and decoding, and synchronous, limited-constructor, reusable, and
typed Finish forms in Client.Buckets. Gate the complete pinned structural
graph, signed native/lightweight sockets, normalization, bounds, and coverage,
and document the prose-only cross-field policy boundary.
5876862c → updated to 0673cdadindex eecf5c641 source commit
0673cdadClients can read bucket replication configuration but cannot replace the same complete graph through the provider-owned composable boundary. Callers would otherwise have to assemble signing, one-shot body ownership, response bounds, and mutation certainty themselves, risking replay or an unsound definite-outcome classification.
Solution: Add nonreplaying provider-owned replication replacement
Serialize the reviewed presence-sensitive replication graph under caller limits, prepare and sign the exact modeled request, and expose low-level, limited-constructor, operation-last, typed Finish, and synchronous forms over one state machine. Add deterministic model/XML/certainty coverage, native and lightweight signed socket evidence, maintained documentation, and coverage bookkeeping.
0673cdad → updated to aff992c0index d1470f675 source commits
ded98183This repository is pinned to an older flyology-ada/agents revision, so its generated instructions and installed skills omit the current shared assurance workflows.
Solution: Update the shared agents dependency
Advance the lockfile to agents revision aa7c026, reinstall the package graph, and regenerate the Codex instructions.
c268566aUpdate shared agents to aa7c026
4da54070This consumer remains pinned to the prior shared agents revision, whose TLA+ workflow includes repository architecture constraints unrelated to using the skill.
Solution: Update the shared agents lock
Advance flyology-ada/agents to revision 6a97e7a and record the refined agent-focused TLA+ skill deployment hashes.
1ec12695Update shared agents to 6a97e7a
aff992c0Each new S3 operation manually repeats pinned-model parsing, coverage bookkeeping, verifier registration, corpus ordering, and documentation updates. The duplicated inventories have already drifted and make completing the remaining 116-operation surface slower and easier to misclassify.
Solution: establish a reviewed operation and evidence registry
Add a human-owned TOML inventory for all 116 pinned operations, a shared uv-driven model/audit/scaffold/qualify tool, generated coverage counts, test registration, and documentation, plus layer-specific executable-evidence checks and negative oracles. Preserve every existing coverage state and corpus order, use GetBucketReplication as the reviewed equivalence canary, and leave legacy Ada implementations unchanged for incremental family migration.
aff992c0 → updated to ace67878index 77fa38222 source commits
06c6331aProvider operations repeat response buffering, singleton-header validation, hidden HTTP child drain, cancellation, restart cleanup, and typed terminal storage. This slows operation coverage and lets semantically equivalent read paths drift.
Solution: Share the owner-driven bounded REST/XML read family
Add an implementation generic for invariant transport and ownership mechanics, then migrate GetBucketReplication as the retrospective canary. Keep provider-owned public signatures, request preparation, decoding, classification, and synchronous-over-composable behavior unchanged, and record executable shared-family provenance in the operation registry.
ace67878The root test gate invokes the generated S3 operation registry checks through uv, but the hosted test jobs do not install uv. Both Linux and macOS therefore fail after a successful Ada build, leaving the automation foundation unqualified.
Solution: Install and enforce the reviewed uv toolchain in CI
Install exact uv 0.11.28 through immutable setup-uv v9.0.0 in the integrity and test jobs. Extend workflow policy and its negative oracle so both the action and tool version remain pinned.
ace67878 → updated to f1a2a7b7index b8287ca41 source commit
f1a2a7b7Each bounded REST/XML operation manually duplicates exact HTTP targets, signed headers, malformed XML fixtures, socket sequencing, and test registration. Those copies can drift from the pinned Botocore model and make each operation expensive to qualify.
Solution: Generate data-driven qualification from reviewed decisions
Derive signed request plans and routine negative XML cases from the pinned model, run them through a shared uv loopback driver and small typed Ada adapter, and register executable evidence from the operation manifest. GetBucketReplication is the retrospective 23-case equivalence canary, while the prospective metrics query oracle verifies reusable canonical query generation.
f1a2a7b7 → updated to f65afbf2index a6c274401 source commit
f65afbf2The shared bounded REST/XML state machine and operation registry establish reusable transport and model evidence, but the first new operation must prove that reviewed API decisions, generated negative XML, signed requests, coverage registration, and documentation can advance without restoring per-operation shell bookkeeping. GetBucketMetricsConfiguration remains partial and invisible to callers until that path is exercised end to end.
Solution: qualify GetBucketMetricsConfiguration through the shared family
Add the strict presence-preserving metrics codec and provider-owned low-level, composable, restart, Finish, and synchronous forms over the shared state machine. Record only reviewed semantics and fixtures in the registry, derive the model-owned request and 17 malformed XML cases, run them through a small typed socket adapter, and make the independent coverage oracle discover test trees instead of a maintained filename list. Preserve explicit caller limits and the model's lack of an inferred filter-cardinality or identifier-content constraint.
f65afbf2 → updated to 4e5af762index 51207f331 source commit
4e5af762GetBucketAnalyticsConfiguration is tracked by the pinned S3 model but has no
typed provider API or executable client evidence. Callers cannot perform one
bounded named analytics read through the shared owner-driven state machine,
and the operation registry therefore retains partial client coverage.
Solution: qualify analytics reads through the shared REST/XML family
Add a strict model-aligned analytics codec, exact low-level preparation and
decoding, and provider-owned limited, restartable, typed Finish, and
synchronous forms. Register reviewed signed cases and model-derived negative
XML evidence, regenerate the coverage artifacts, and document the resulting
client-only compatibility boundary.
4e5af762 → updated to 7cbcb44eindex 826970c61 source commit
7cbcb44eGetBucketIntelligentTieringConfiguration is tracked by the pinned S3 model
but has no typed provider API or executable client evidence. Callers cannot
perform a bounded named configuration read through the shared owner-driven
state machine, and registry coverage remains partial.
Solution: qualify Intelligent-Tiering reads through the shared REST/XML family
Add a strict model-aligned codec, exact low-level preparation and decoding,
and provider-owned limited, restartable, typed Finish, and synchronous forms.
Register reviewed signed cases and model-derived negatives, regenerate
coverage artifacts, and document client-only compatibility.
7cbcb44e → updated to 9fb63d01index 2fe4325e1 source commit
9fb63d01Callers cannot read named S3 inventory configurations through the provider-
centric synchronous and composable APIs. The coverage registry therefore keeps
the operation partial even though it belongs to the established bounded
REST/XML read family.
Solution: add the typed inventory read through the shared family
Add the strict complete inventory codec, exact low-level preparation and
decoding, provider-owned constructor/restart/Finish/synchronous forms, and
model-derived negative and signed-socket qualification. Record the reviewed
model decisions and executable evidence without claiming backend, server, or
external-provider support.
9fb63d01 → updated to 10d46346index 400ea6431 source commit
10d46346The current development line leaves bucket logging as a partial generated client with no provider-owned composable operation or executable corpus. Its modeled payload also uses a wire-root alias, a nonflattened grant list, and required namespaced attributes that the operation verifier cannot yet qualify without manual negative-case work.
Solution: Add the strict logging client and extend model-driven qualification
Add the provider-centric low-level, composable, and synchronous Get_Logging forms over the shared bounded REST/XML read state machine. Decode the complete presence-preserving logging graph, disabled state, exact grantee attributes and enum domains, and modeled errors without adding defaults or replay policy. Extend the uv tool to verify structurally equivalent payload aliases, namespaced attributes, and nonflattened list members, then generate and execute the signed and negative XML evidence and update the coverage and architecture records.
10d46346 → updated to f5618821index d2404a6d1 source commit
f5618821The public client and signed corpus leave bucket website configuration unreadable, and the model verifier rejects the operation's documented payload alias even though its wire structure matches the reviewed WebsiteConfiguration shape. Repeating another handwritten bounded REST/XML read would also preserve the rollout bottleneck.
Solution: Add a strict provider-owned website read and model-driven evidence
Add the presence-preserving Website configuration codec, low-level and provider-owned composable/synchronous calls, generated negative XML and signed-socket qualification, and registry coverage. Verify payload aliases structurally while ignoring documentation-only differences and retaining all wire traits, so the shared bounded read family can qualify the operation without semantic drift.
f5618821 → updated to d0fd10d7index fdc77edc1 source commit
d0fd10d7The remaining bucket-configuration list operations share strict REST/XML page mechanics, request continuation projection, bounded response ownership, and signed qualification structure. Reimplementing those mechanics operation by operation slows coverage work and risks semantic or evidence drift.
Solution: Add a shared strict page family and qualify Metrics as its canary
Add a private paginated REST/XML envelope generic, reuse the reviewed metrics item codec, and expose provider-owned one-page synchronous and composable ListBucketMetricsConfigurations calls over the existing bounded read state machine. Record reviewed manifest decisions and generate the model-derived negative XML, signed socket, coverage, registration, and documentation evidence, while keeping continuation caller-driven and public policy unchanged.
d0fd10d7 → updated to f28a3fb6index b79170c41 source commit
f28a3fb6ListBucketAnalyticsConfigurations is still represented only by model inventory, so callers lack a qualified one-page provider API and each future implementation risks repeating page-envelope, ownership, and evidence mechanics.
Solution: Qualify Analytics through the paginated REST/XML family
Reuse the reviewed analytics item decoder inside the shared strict page envelope, add exact low-level and provider-owned synchronous/composable calls over the existing bounded state machine, and generate the model-derived negative, signed-socket, coverage, registration, and documentation evidence from reviewed manifest decisions.
0.1.0-devTLA+ trace generation and Ada conformance replay harness
ffc459bc → updated to b03cb81dindex 7c8e721f1 source commit
b03cb81dThe public trace API only accepts paths and only writes prefixes to files, while result artifacts can be encoded but not strictly decoded. Consumers embedding shared evidence in their own process envelopes must use temporary files or duplicate the versioned JSON contracts, and the schema invalid-trace verdict is not representable in Ada.
Solution: expose strict in-memory trace and result boundaries
Add bounded trace Parse and canonical Image operations with the file APIs delegating to them. Add strict result decoding with trace identity binding, represent every schema verdict, document the envelope workflow, and cover canonical round trips plus unknown and duplicate member rejection.
b03cb81d → updated to e75b8483index 3a3583971 source commit
e75b8483Trace version 1 binds only one caller-supplied source digest and a configuration name. Consumers with EXTENDS closures can therefore regenerate evidence after a helper or configuration change without changing the recorded model identity. Typed generation and trace normalization also compute provenance through different paths.
Solution: bind the resolved model closure and configuration bytes
Add trace version 2 and an authoritative SANY-backed model identity command. Share the constrained semantic export across normalization and typed generation, retain strict trace version 1 reads, and cover deterministic closure hashing, configuration hashing, schemas, compatibility, and the actual TLC/Ada/TLAPM example.
e75b8483 → updated to faf06c48index ea2ae2051 source commit
faf06c48The result/1 contract records the exact trace and stable failure identity, but a process holding only those artifacts cannot report the adapter outcome and state at the completed divergent comparison. Current consumers must remain able to emit and strictly parse result/1 while they migrate independently.
Solution: Add an explicit bounded result/2 comparison contract
Introduce a source-compatible Replay_Result_V2 API with typed initial and step observations, strict version-specific encoding and decoding, canonical embedded JSON, schema and fixtures, human reporting, and replay/CLI overloads. Keep expected values authoritative in the SHA-256-bound trace, reject impossible observation/verdict combinations, and leave adapter errors and invalid traces observation-free.
a95cdc5d on GitHub
0.1.0-devTLA+ trace generation and Ada conformance replay harness
flyology_json =0.1.0-devgnat >=13 & <17dab710a4 on GitHub
0.1.0-devIncremental bounded JSON parsing and writing for Ada
0.1.1-devExperimental GNAT runtime extension for native and lightweight Ada tasks
3859f412 → updated to ca8c23a8index 30ef277e3 source commits
f639c4bfSupervisor monitor registration and scoped task-result attachment can publish a retained runtime resource before their Ada controlled guards own it. Abort in that boundary can strand a monitor slot or sidecar reference, preventing later exact-generation observation and making remoting lifecycle cleanup unsafe.
Solution: Publish lifecycle wait ownership atomically
Move supervisor guard ownership into the protected monitor-registration cut, defer abort through task-result retain publication, and add statically elided deterministic abort barriers. The focused fixtures prove exact cleanup and capacity reuse for Static, Families, task-id waits, and retained-monitor waits while production builds contain no hook references.
954a4392Remoting needs a bounded one-shot admission owner and an exact lifecycle observation that survive abort, shutdown, restart, and slot reuse. Families currently queue accepted requests immediately, while ordinary generation waits cannot retain one admission epoch across replacement publication.
Solution: Add prepared family admissions and scoped exact observation
Add the optional Prepared_Admissions child over existing Family slots and monitor capacity, abort-safe prepare/commit/release/cancel ownership cuts, coherent exact-epoch lifecycle observation, bounded wake publication, deterministic fault/lifetime/generation coverage, and public architecture documentation.
ca8c23a8Add prepared family admissions and scoped lifecycle observation
ca8c23a8 → updated to bd95746dindex 711715e92 source commits
49960169Callers that must authorize, bind, or publish an exact supervised child identity while a request remains prepared cannot inspect its Child_Handle until ownership moves into Started_Admission. Remoting task admission therefore cannot validate the reserved controller, child, and generation before committing the launch.
Solution: Expose the prepared claim's exact first handle
Add the Start_Claim overload of First_Handle with the active-owner precondition used by Started_Admission. Document that Commit_Start transfers admission ownership while preserving the handle identity, and verify exact identity plus rollback/reprepare generation advancement.
bd95746dExpose prepared admission handle before commit
0.1.0-devCaller-owned memory allocation algorithms for Ada
3859f412 → updated to ca8c23a8index 30ef277e3 source commits
f639c4bfSupervisor monitor registration and scoped task-result attachment can publish a retained runtime resource before their Ada controlled guards own it. Abort in that boundary can strand a monitor slot or sidecar reference, preventing later exact-generation observation and making remoting lifecycle cleanup unsafe.
Solution: Publish lifecycle wait ownership atomically
Move supervisor guard ownership into the protected monitor-registration cut, defer abort through task-result retain publication, and add statically elided deterministic abort barriers. The focused fixtures prove exact cleanup and capacity reuse for Static, Families, task-id waits, and retained-monitor waits while production builds contain no hook references.
954a4392Remoting needs a bounded one-shot admission owner and an exact lifecycle observation that survive abort, shutdown, restart, and slot reuse. Families currently queue accepted requests immediately, while ordinary generation waits cannot retain one admission epoch across replacement publication.
Solution: Add prepared family admissions and scoped exact observation
Add the optional Prepared_Admissions child over existing Family slots and monitor capacity, abort-safe prepare/commit/release/cancel ownership cuts, coherent exact-epoch lifecycle observation, bounded wake publication, deterministic fault/lifetime/generation coverage, and public architecture documentation.
ca8c23a8Add prepared family admissions and scoped lifecycle observation
ca8c23a8 → updated to bd95746dindex 711715e92 source commits
49960169Callers that must authorize, bind, or publish an exact supervised child identity while a request remains prepared cannot inspect its Child_Handle until ownership moves into Started_Admission. Remoting task admission therefore cannot validate the reserved controller, child, and generation before committing the launch.
Solution: Expose the prepared claim's exact first handle
Add the Start_Claim overload of First_Handle with the active-owner precondition used by Started_Admission. Document that Commit_Start transfers admission ownership while preserving the handle identity, and verify exact identity plus rollback/reprepare generation advancement.
bd95746dExpose prepared admission handle before commit
0.1.1-devAdaptive nanosecond-scale benchmarking for Ada
3859f412 → updated to ca8c23a8index 30ef277e3 source commits
f639c4bfSupervisor monitor registration and scoped task-result attachment can publish a retained runtime resource before their Ada controlled guards own it. Abort in that boundary can strand a monitor slot or sidecar reference, preventing later exact-generation observation and making remoting lifecycle cleanup unsafe.
Solution: Publish lifecycle wait ownership atomically
Move supervisor guard ownership into the protected monitor-registration cut, defer abort through task-result retain publication, and add statically elided deterministic abort barriers. The focused fixtures prove exact cleanup and capacity reuse for Static, Families, task-id waits, and retained-monitor waits while production builds contain no hook references.
954a4392Remoting needs a bounded one-shot admission owner and an exact lifecycle observation that survive abort, shutdown, restart, and slot reuse. Families currently queue accepted requests immediately, while ordinary generation waits cannot retain one admission epoch across replacement publication.
Solution: Add prepared family admissions and scoped exact observation
Add the optional Prepared_Admissions child over existing Family slots and monitor capacity, abort-safe prepare/commit/release/cancel ownership cuts, coherent exact-epoch lifecycle observation, bounded wake publication, deterministic fault/lifetime/generation coverage, and public architecture documentation.
ca8c23a8Add prepared family admissions and scoped lifecycle observation
ca8c23a8 → updated to bd95746dindex 711715e92 source commits
49960169Callers that must authorize, bind, or publish an exact supervised child identity while a request remains prepared cannot inspect its Child_Handle until ownership moves into Started_Admission. Remoting task admission therefore cannot validate the reserved controller, child, and generation before committing the launch.
Solution: Expose the prepared claim's exact first handle
Add the Start_Claim overload of First_Handle with the active-owner precondition used by Started_Admission. Document that Commit_Start transfers admission ownership while preserving the handle identity, and verify exact identity plus rollback/reprepare generation advancement.
bd95746dExpose prepared admission handle before commit
0.1.1-devCache-line-aware storage and cache information for Ada
3859f412 → updated to ca8c23a8index 30ef277e3 source commits
f639c4bfSupervisor monitor registration and scoped task-result attachment can publish a retained runtime resource before their Ada controlled guards own it. Abort in that boundary can strand a monitor slot or sidecar reference, preventing later exact-generation observation and making remoting lifecycle cleanup unsafe.
Solution: Publish lifecycle wait ownership atomically
Move supervisor guard ownership into the protected monitor-registration cut, defer abort through task-result retain publication, and add statically elided deterministic abort barriers. The focused fixtures prove exact cleanup and capacity reuse for Static, Families, task-id waits, and retained-monitor waits while production builds contain no hook references.
954a4392Remoting needs a bounded one-shot admission owner and an exact lifecycle observation that survive abort, shutdown, restart, and slot reuse. Families currently queue accepted requests immediately, while ordinary generation waits cannot retain one admission epoch across replacement publication.
Solution: Add prepared family admissions and scoped exact observation
Add the optional Prepared_Admissions child over existing Family slots and monitor capacity, abort-safe prepare/commit/release/cancel ownership cuts, coherent exact-epoch lifecycle observation, bounded wake publication, deterministic fault/lifetime/generation coverage, and public architecture documentation.
ca8c23a8Add prepared family admissions and scoped lifecycle observation
ca8c23a8 → updated to bd95746dindex 711715e92 source commits
49960169Callers that must authorize, bind, or publish an exact supervised child identity while a request remains prepared cannot inspect its Child_Handle until ownership moves into Started_Admission. Remoting task admission therefore cannot validate the reserved controller, child, and generation before committing the launch.
Solution: Expose the prepared claim's exact first handle
Add the Start_Claim overload of First_Handle with the active-owner precondition used by Started_Admission. Document that Commit_Start transfers admission ownership while preserving the handle identity, and verify exact identity plus rollback/reprepare generation advancement.
bd95746dExpose prepared admission handle before commit
0.1.1-devBounded in-memory tracing and gauges for Ada
3859f412 → updated to ca8c23a8index 30ef277e3 source commits
f639c4bfSupervisor monitor registration and scoped task-result attachment can publish a retained runtime resource before their Ada controlled guards own it. Abort in that boundary can strand a monitor slot or sidecar reference, preventing later exact-generation observation and making remoting lifecycle cleanup unsafe.
Solution: Publish lifecycle wait ownership atomically
Move supervisor guard ownership into the protected monitor-registration cut, defer abort through task-result retain publication, and add statically elided deterministic abort barriers. The focused fixtures prove exact cleanup and capacity reuse for Static, Families, task-id waits, and retained-monitor waits while production builds contain no hook references.
954a4392Remoting needs a bounded one-shot admission owner and an exact lifecycle observation that survive abort, shutdown, restart, and slot reuse. Families currently queue accepted requests immediately, while ordinary generation waits cannot retain one admission epoch across replacement publication.
Solution: Add prepared family admissions and scoped exact observation
Add the optional Prepared_Admissions child over existing Family slots and monitor capacity, abort-safe prepare/commit/release/cancel ownership cuts, coherent exact-epoch lifecycle observation, bounded wake publication, deterministic fault/lifetime/generation coverage, and public architecture documentation.
ca8c23a8Add prepared family admissions and scoped lifecycle observation
ca8c23a8 → updated to bd95746dindex 711715e92 source commits
49960169Callers that must authorize, bind, or publish an exact supervised child identity while a request remains prepared cannot inspect its Child_Handle until ownership moves into Started_Admission. Remoting task admission therefore cannot validate the reserved controller, child, and generation before committing the launch.
Solution: Expose the prepared claim's exact first handle
Add the Start_Claim overload of First_Handle with the active-owner precondition used by Started_Admission. Document that Commit_Start transfers admission ownership while preserving the handle identity, and verify exact identity plus rollback/reprepare generation advancement.
bd95746dExpose prepared admission handle before commit
0.1.0-devMemory-node topology reporting for Ada
3859f412 → updated to ca8c23a8index 30ef277e3 source commits
f639c4bfSupervisor monitor registration and scoped task-result attachment can publish a retained runtime resource before their Ada controlled guards own it. Abort in that boundary can strand a monitor slot or sidecar reference, preventing later exact-generation observation and making remoting lifecycle cleanup unsafe.
Solution: Publish lifecycle wait ownership atomically
Move supervisor guard ownership into the protected monitor-registration cut, defer abort through task-result retain publication, and add statically elided deterministic abort barriers. The focused fixtures prove exact cleanup and capacity reuse for Static, Families, task-id waits, and retained-monitor waits while production builds contain no hook references.
954a4392Remoting needs a bounded one-shot admission owner and an exact lifecycle observation that survive abort, shutdown, restart, and slot reuse. Families currently queue accepted requests immediately, while ordinary generation waits cannot retain one admission epoch across replacement publication.
Solution: Add prepared family admissions and scoped exact observation
Add the optional Prepared_Admissions child over existing Family slots and monitor capacity, abort-safe prepare/commit/release/cancel ownership cuts, coherent exact-epoch lifecycle observation, bounded wake publication, deterministic fault/lifetime/generation coverage, and public architecture documentation.
ca8c23a8Add prepared family admissions and scoped lifecycle observation
ca8c23a8 → updated to bd95746dindex 711715e92 source commits
49960169Callers that must authorize, bind, or publish an exact supervised child identity while a request remains prepared cannot inspect its Child_Handle until ownership moves into Started_Admission. Remoting task admission therefore cannot validate the reserved controller, child, and generation before committing the launch.
Solution: Expose the prepared claim's exact first handle
Add the Start_Claim overload of First_Handle with the active-owner precondition used by Started_Admission. Document that Commit_Start transfers admission ownership while preserving the handle identity, and verify exact identity plus rollback/reprepare generation advancement.
bd95746dExpose prepared admission handle before commit
0.1.0-devS3 client and pluggable object-storage server for Flyology
0761f9e2 → updated to aa799686index 16142be31 source commit
aa799686Strict Linux C compilation hides kill without an explicit POSIX feature contract, the proof project cannot locate an indirect Flyology project in Alire's shared cache, and shallow test checkouts cannot validate retained benchmark ancestry. The first public main CI therefore fails despite the locally qualified implementation.
Solution: make clean-checkout requirements explicit
Expose the POSIX.1-2008 declaration set locally in the durability bridge, prepend Alire's exported Flyology root only for maintained proof invocations, and fetch complete history for the test job that validates retained provenance.
aa799686 → updated to 98383e48index dee928e81 source commit
98383e48Linux SQLite consumers inherit an unnecessary libm link from the optional FTS5 module, while the proof gate imports Flyology through a transitive cache location that Alire does not expose consistently in clean hosted workspaces. This prevents the published development line from completing its maintained SQLite and SPARK gates.
Solution: narrow SQLite features and import the direct proof closure
Disable the unused FTS5 extension instead of adding a compensating linker dependency. Make the proof project import the direct HTTP and XML project dependencies, and let the maintained script invoke GNATprove directly without reconstructing a transitive cache path.
98383e48 → updated to 3455cde3index 8e99188e1 source commit
3455cde3A clean hosted proof job invokes Alire execution before dependency build roots and generated configuration exist. The resulting GPR search path names directories that have not been synchronized, so GNATprove cannot load the direct HTTP and XML project closure.
Solution: materialize the proof dependency closure before analysis
Run Alire through the pre-build stage before GNATprove. This synchronizes dependencies and runs configuration actions without compiling the Object Storage library; GNATprove remains the analyzer for the selected proof units.
3455cde3 → updated to 1978275bindex 27baef815 source commits
09e6eac2The bucket lifecycle client exposes deletion only through blocking execution, so callers cannot share a Completion_Set and HTTP owner while retaining typed admission certainty across a possibly admitted mutation.
Solution: Add one provider-owned non-replaying DeleteBucket operation
Add the limited constructor, operation-last reusable initiation, typed Finish, and synchronous wait on one state machine. Use a non-rewindable empty source, bounded response retention, conservative deletion certainty, signed socket and normalization corpora, and maintainer documentation.
22fec71cThe bucket provider exposes only blocking location discovery even though callers need the raw bounded response while coordinating related operations in one Completion_Set. They must either block outside their state machine or bypass the provider-owned certainty, cancellation, and response-decoding contract.
Solution: add one provider-owned bounded location read
Add limited-root and operation-last Get_Location forms, a typed Finish result, and the matching prepared low-level initiator. Make the typed synchronous and legacy convenience overloads wait on the same bounded owner-driven state machine, preserve established defaults and legacy region normalization, retain no borrowed request input after signing, and leave safe GET recovery to Flyology HTTP. Gate exact response/failure normalization, expected-owner signing, fragmented XML, restart, documentation, full deterministic tests, and the warning-strict 936-check proof campaign.
c0bae328Bucket-version reconciliation is available only through a blocking call, so structured callers cannot share a completion set, deadline, cancellation owner, or reusable operation while preserving the modeled response and HTTP failure evidence.
Solution: add one provider-owned bounded versioning read
Add limited-root and operation-last Get_Versioning forms with typed Finish in Client.Buckets, drive the prepared request through the shared HTTP state machine, retain XML under the versioning codec bound, preserve the established synchronous behavior, and qualify normalization, restart, documentation, and signed socket behavior.
39f97288Bucket-version updates are available only through blocking execution, so structured callers cannot own deadline, cancellation, and restart state or retain typed admission certainty for reconciliation.
Solution: add one provider-owned nonreplaying versioning mutation
Add limited-root and operation-last Set_Versioning_Configuration with typed Finish, move synchronous execution onto the same owned one-shot source, preserve exact certainty mapping, and qualify normalization, restart, documentation, signed sockets, and proof.
1978275bBucket-policy retrieval is available only through blocking execution, so structured callers cannot retain typed HTTP state, caller-selected response bounds, cancellation, and restart ownership in a larger operation graph.
Solution: add one provider-owned bounded policy read
Add limited-root and operation-last Get_Policy with typed Finish, route the synchronous parameter-record overload through the same state machine, enforce exact prepared-operation identity, and qualify normalization, bounds, restart, documentation, signed sockets, and proof.
1978275b → updated to 179b16c4index ade6ebbd9 source commits
0da1b4f7Callers can read bucket policies compositionally, but replacement and deletion still use separate blocking transport paths. That prevents provider-centric operation reuse, obscures admission certainty after ambiguous mutations, and leaves a socket-corpus startup failure able to strand clients before the server publishes its port.
Solution: add provider-owned nonreplaying policy mutations
Add limited Set_Policy and Delete_Policy roots, operation-last reusable initiation, typed Finish/results, exact low-level prepared-operation binding, and synchronous waits over the same one-shot state machines. Preserve owned payloads, caller-selected bounds, admission certainty, and read-only reconciliation rules; extend the signed corpus and make pre-publication server failure wake waiting clients deterministically.
5503d9d0The implementation manifest labels the Flyology files lane as not power durable even though its server uses the production durability policy. Campaign metadata also leaves that selected policy implicit, allowing later comparisons to overstate or misclassify durability equivalence.
Solution: Pin and retain the production durability selection
Label the matrix lane power-loss-durable, pass Power_Loss_Durable explicitly to the files server, record the exact commit policy in both retained matrix metadata streams, and gate the metadata field. Clarify the guide while leaving immutable historical evidence unchanged.
3453828cPutPublicAccessBlock, GetPublicAccessBlock, and DeletePublicAccessBlock are available through synchronous helpers but not through the provider-owned operation model. Callers cannot compose these security controls with their completion sets, and mutation failures lack the typed certainty needed to choose read-only reconciliation without replay.
Solution: Add provider-owned public access block operations
Add limited constructors, operation-last restart procedures, typed Finish results, and synchronous waits on the same Client.Buckets state machines. Keep mutation bodies non-rewindable, preserve admission certainty and bounded same-response reads, reject mismatched prepared operations before HTTP admission, and cover lifecycle, restart, certainty, and documentation contracts.
551d514dCallers can retrieve bucket ownership controls only through the blocking low-level executor, so owner-driven operation graphs cannot compose the read, preserve typed HTTP failure context, or reuse an established parent operation.
Solution: colocate bounded Ownership Controls reads with the Buckets provider
Add limited and operation-last provider forms, typed Finish and synchronous waiting over the same state machine, an exact prepared-request initiator, bounded same-response decoding, restart and operation-identity tests, and matching architecture and compatibility evidence.
07269323PutBucketOwnershipControls is available only through a blocking executor, so owner-driven callers cannot retain typed admission certainty or reconcile ambiguous publication without leaving the provider API.
Solution: add a non-replaying Ownership Controls mutation state machine
Colocate limited, operation-last, Finish, and synchronous forms in Client.Buckets; expose an exact low-level initiator; retain serialized bytes through terminal drain; classify only response-observed exact rejections as definitely not applied; and gate operation identity, restart, wire behavior, and the complete admission-certainty matrix.
33b28a12DeleteBucketOwnershipControls still uses the generic blocking configuration path, so composed callers cannot retain typed admission certainty, reuse an established operation, or reconcile an ambiguous deletion through the owning provider.
Solution: complete the provider-owned Ownership Controls trio
Add a non-replaying Delete operation with limited and operation-last forms, typed Finish and synchronous waiting; rewrite the existing convenience call through that state machine; expose exact low-level initiation; and gate operation identity, restart, wire behavior, and exhaustive certainty normalization.
da5c057fGetBucketEncryption is available only through the blocking executor, so owner-driven callers cannot compose its bounded response, cancellation, and deadline into an existing completion graph.
Solution: Add a provider-owned Get_Encryption operation
Add the exact low-level prepared initiator, limited and reusable provider overloads, typed Finish and synchronous wait over one bounded state machine. Cover exact-operation rejection, restart, response normalization, certainty, signed socket behavior, and document the expanded provider surface.
e00ca596DeleteBucketEncryption uses the blocking configuration dispatcher, so owner-driven callers cannot compose its one-shot mutation, deadline, cancellation, and admission certainty into an existing completion graph.
Solution: Add a provider-owned Delete_Encryption operation
Add the exact low-level initiator, limited and reusable provider overloads, typed Finish, and synchronous waits over one non-replaying state machine. Preserve the legacy Delete_Outcome surface through that same engine, remove its dead blocking branch, and cover exact binding, restart, certainty, response classes, and signed socket behavior.
179b16c4The five no-pin solve roots now select the published HTTP/QUIC 0.1.3-dev source that removes hook preprocessing, while CI and maintained prose still assert the previous flattened-client origin.
Solution: Advance the tracked HTTP origin qualification
Update CI and maintainer documentation to exact eb09a80, retain exact current-dev constraints and no external pins, and align nearby composable terminology with the provider-centric API.
179b16c4 → updated to 4401e0ecindex 4f7124b28 source commits
db16a412The existing CORS client can read and delete configurations, but callers cannot publish a complete configuration through the provider-owned operation model. They cannot retain typed admission certainty, restart an established component, or prove that the exact modeled request graph is covered without falling back to a separate blocking path.
Solution: add one provider-owned nonreplaying CORS mutation
Serialize the pinned flattened CORS graph within caller-selected XML limits, prepare and execute one-shot signed requests, and colocate limited, operation-last, typed Finish, and synchronous Set_CORS forms in Client.Buckets. Add reciprocal model ledgers, deterministic and signed socket corpora, certainty normalization, coverage accounting, GNATdoc, and maintainer qualification evidence.
df9d0d49GetBucketCors and DeleteBucketCors stop at synchronous execution even though their provider already owns exact prepared requests and bounded response decoding. Callers cannot reuse them inside completion-set state machines, and synchronous deletion does not share typed admission certainty with the composable client vocabulary.
Solution: colocate composable CORS reads and deletions with Client.Buckets
Add exact low-level initiators and provider-owned limited constructors, operation-last reusable procedures, typed Finish results, and synchronous waits on the same state machines. Preserve bounded decoding, non-replaying empty deletion bodies, admission certainty, and conservative outcome mapping, with signed socket, restart, mismatch, capacity, documentation, and normalization coverage.
b30f0812GetObjectLegalHold and PutObjectLegalHold stop at blocking low-level execution, forcing callers to leave provider-owned completion sets and obscuring mutation admission certainty.
Solution: colocate owner-driven Legal Hold operations in Client.Objects
Add exact prepared-request initiators, limited constructors, operation-last restart, typed Finish, shared synchronous waits, an owned nonreplayable mutation source, bounded response sinks, certainty normalization, signed socket and negative corpora, and maintainer documentation.
1160fd60GetObjectRetention and PutObjectRetention are available only through blocking client entry points, so completion-set state machines cannot compose exact-version retention reads or mutations without inventing a parallel wrapper. That obscures provider ownership and prevents typed admission certainty from flowing through caller-owned scheduling.
Solution: colocate Retention operations with Client.Objects
Add exact prepared-request initiators plus provider-owned limited constructors, operation-last reusable initiation, typed Finish, and synchronous waits driven by the same bounded state machines. Own the Put XML in a non-rewindable source, preserve conservative post-admission mutation certainty, require generation-bound read reconciliation, and gate restart, exact operation binding, normalization, and one-past response limits under native and lightweight owners.
aa0a3693GetObjectLockConfiguration and PutObjectLockConfiguration expose only blocking execution, so completion-set state machines cannot own their lifetime, drain, certainty, and bounded response handling through the bucket provider.
Solution: colocate owner-driven Object Lock configuration operations in Client.Buckets
Add exact Low_Level prepared-request initiators, limited constructors, operation-last reusable procedures, typed Finish results, and synchronous waits over the same state machines. Preserve bounded same-response reads, one-shot non-replaying mutation ownership, conservative admission certainty, and read-only reconciliation, with strict socket, certainty, documentation, and API gates.
d1dee5beCallers can prepare and execute bucket-encryption updates synchronously, but they cannot schedule the mutation beside other owner-driven operations without rebuilding its signing, body-lifetime, and publication-certainty rules.
Solution: add one nonreplaying provider-owned encryption mutation
Serialize the pinned presence-sensitive configuration into caller-bounded XML, retain its exact signed bytes in the parent operation, expose limited and operation-last Set_Encryption forms with typed Finish, and make the synchronous form wait on that same state machine. Add reciprocal model inventories, deterministic and signed-socket corpora, certainty normalization coverage, qualification evidence, and honest client-only compatibility claims.
0956dcd3The reviewed CORS, Object Lock configuration, Legal Hold, and Retention operation families live on a parallel lineage, while main independently adds PutBucketEncryption. Leaving the lineages separate prevents exact-tree qualification and publication of the complete provider-owned surface.
Solution: integrate both composable lineages without narrowing either contract
Retain every prepared initiator, provider-owned limited constructor, operation-last restart, typed Finish, synchronous wait, corpus, and qualification record from both sides. Reconcile the operation inventory at 52 provider operations and 49 prepared initiators while preserving the explicit three-call projection difference and all nonreplay, ownership, deadline, and certainty rules.
4401e0ecConfiguration-rejection cases can reach credential bootstrap before failing, which leaves a generated credentials file in the repository root and dirties an otherwise clean qualification tree.
Solution: isolate rejected-startup credentials in the test run directory
Give every expected-failure launch its own credentials path under RUN_ROOT so bootstrap artifacts are removed with the maintained test workspace.
4401e0ec → updated to 99706cceindex 91cc2b501 source commit
99706ccePutBucketCors, GetBucketCors, and DeleteBucketCors have complete clients and signed corpora, but authenticated servers cannot execute them and no backend retains their state. Deployments therefore cannot provide the full configuration lifecycle even though callers can construct every request.
Solution: complete bucket CORS across backends and server
Add the backend-neutral canonical-document contract and implement atomic memory, files, and SQLite persistence. Route authenticated server PUT, GET, and DELETE with strict query, owner, checksum, XML, and absence handling. Advance SQLite to schema 13, add migration, corruption, symlink, capacity, restart, and 132-case abrupt-crash evidence, update compatibility and coverage, and qualify the exact tree through full tests, server/provider gates, GNATdoc, and 936/936 SPARK checks.
99706cce → updated to 6ec20483index 4414c63a1 source commit
6ec20483The qualified client and ACL codec cannot read bucket or object ACLs from Flyology servers, even though the existing single-tenant mutation profile admits only private ownership. Raw subresource routing must also prevent encoded ACL queries from falling through to ordinary mutations.
Solution: Derive owner-only ACL responses in the S3 application
Route authenticated GetBucketAcl and version-selecting GetObjectAcl calls to backend existence reads, synthesize the exact CanonicalUser FULL_CONTROL policy from the stable tenant principal, and reject ACL mutations without publication. Add strict query, authorization, owner, payer, body, error-classification, encoded-subresource, in-process, and real-server socket gates; promote only the server coverage cells while leaving ACL persistence unclaimed.
0.1.0-devSupervised S3 server and management console for Flyology Object Storage
0761f9e2 → updated to aa799686index 16142be31 source commit
aa799686Strict Linux C compilation hides kill without an explicit POSIX feature contract, the proof project cannot locate an indirect Flyology project in Alire's shared cache, and shallow test checkouts cannot validate retained benchmark ancestry. The first public main CI therefore fails despite the locally qualified implementation.
Solution: make clean-checkout requirements explicit
Expose the POSIX.1-2008 declaration set locally in the durability bridge, prepend Alire's exported Flyology root only for maintained proof invocations, and fetch complete history for the test job that validates retained provenance.
aa799686 → updated to 98383e48index dee928e81 source commit
98383e48Linux SQLite consumers inherit an unnecessary libm link from the optional FTS5 module, while the proof gate imports Flyology through a transitive cache location that Alire does not expose consistently in clean hosted workspaces. This prevents the published development line from completing its maintained SQLite and SPARK gates.
Solution: narrow SQLite features and import the direct proof closure
Disable the unused FTS5 extension instead of adding a compensating linker dependency. Make the proof project import the direct HTTP and XML project dependencies, and let the maintained script invoke GNATprove directly without reconstructing a transitive cache path.
98383e48 → updated to 3455cde3index 8e99188e1 source commit
3455cde3A clean hosted proof job invokes Alire execution before dependency build roots and generated configuration exist. The resulting GPR search path names directories that have not been synchronized, so GNATprove cannot load the direct HTTP and XML project closure.
Solution: materialize the proof dependency closure before analysis
Run Alire through the pre-build stage before GNATprove. This synchronizes dependencies and runs configuration actions without compiling the Object Storage library; GNATprove remains the analyzer for the selected proof units.
3455cde3 → updated to 1978275bindex 27baef815 source commits
09e6eac2The bucket lifecycle client exposes deletion only through blocking execution, so callers cannot share a Completion_Set and HTTP owner while retaining typed admission certainty across a possibly admitted mutation.
Solution: Add one provider-owned non-replaying DeleteBucket operation
Add the limited constructor, operation-last reusable initiation, typed Finish, and synchronous wait on one state machine. Use a non-rewindable empty source, bounded response retention, conservative deletion certainty, signed socket and normalization corpora, and maintainer documentation.
22fec71cThe bucket provider exposes only blocking location discovery even though callers need the raw bounded response while coordinating related operations in one Completion_Set. They must either block outside their state machine or bypass the provider-owned certainty, cancellation, and response-decoding contract.
Solution: add one provider-owned bounded location read
Add limited-root and operation-last Get_Location forms, a typed Finish result, and the matching prepared low-level initiator. Make the typed synchronous and legacy convenience overloads wait on the same bounded owner-driven state machine, preserve established defaults and legacy region normalization, retain no borrowed request input after signing, and leave safe GET recovery to Flyology HTTP. Gate exact response/failure normalization, expected-owner signing, fragmented XML, restart, documentation, full deterministic tests, and the warning-strict 936-check proof campaign.
c0bae328Bucket-version reconciliation is available only through a blocking call, so structured callers cannot share a completion set, deadline, cancellation owner, or reusable operation while preserving the modeled response and HTTP failure evidence.
Solution: add one provider-owned bounded versioning read
Add limited-root and operation-last Get_Versioning forms with typed Finish in Client.Buckets, drive the prepared request through the shared HTTP state machine, retain XML under the versioning codec bound, preserve the established synchronous behavior, and qualify normalization, restart, documentation, and signed socket behavior.
39f97288Bucket-version updates are available only through blocking execution, so structured callers cannot own deadline, cancellation, and restart state or retain typed admission certainty for reconciliation.
Solution: add one provider-owned nonreplaying versioning mutation
Add limited-root and operation-last Set_Versioning_Configuration with typed Finish, move synchronous execution onto the same owned one-shot source, preserve exact certainty mapping, and qualify normalization, restart, documentation, signed sockets, and proof.
1978275bBucket-policy retrieval is available only through blocking execution, so structured callers cannot retain typed HTTP state, caller-selected response bounds, cancellation, and restart ownership in a larger operation graph.
Solution: add one provider-owned bounded policy read
Add limited-root and operation-last Get_Policy with typed Finish, route the synchronous parameter-record overload through the same state machine, enforce exact prepared-operation identity, and qualify normalization, bounds, restart, documentation, signed sockets, and proof.
1978275b → updated to 179b16c4index ade6ebbd9 source commits
0da1b4f7Callers can read bucket policies compositionally, but replacement and deletion still use separate blocking transport paths. That prevents provider-centric operation reuse, obscures admission certainty after ambiguous mutations, and leaves a socket-corpus startup failure able to strand clients before the server publishes its port.
Solution: add provider-owned nonreplaying policy mutations
Add limited Set_Policy and Delete_Policy roots, operation-last reusable initiation, typed Finish/results, exact low-level prepared-operation binding, and synchronous waits over the same one-shot state machines. Preserve owned payloads, caller-selected bounds, admission certainty, and read-only reconciliation rules; extend the signed corpus and make pre-publication server failure wake waiting clients deterministically.
5503d9d0The implementation manifest labels the Flyology files lane as not power durable even though its server uses the production durability policy. Campaign metadata also leaves that selected policy implicit, allowing later comparisons to overstate or misclassify durability equivalence.
Solution: Pin and retain the production durability selection
Label the matrix lane power-loss-durable, pass Power_Loss_Durable explicitly to the files server, record the exact commit policy in both retained matrix metadata streams, and gate the metadata field. Clarify the guide while leaving immutable historical evidence unchanged.
3453828cPutPublicAccessBlock, GetPublicAccessBlock, and DeletePublicAccessBlock are available through synchronous helpers but not through the provider-owned operation model. Callers cannot compose these security controls with their completion sets, and mutation failures lack the typed certainty needed to choose read-only reconciliation without replay.
Solution: Add provider-owned public access block operations
Add limited constructors, operation-last restart procedures, typed Finish results, and synchronous waits on the same Client.Buckets state machines. Keep mutation bodies non-rewindable, preserve admission certainty and bounded same-response reads, reject mismatched prepared operations before HTTP admission, and cover lifecycle, restart, certainty, and documentation contracts.
551d514dCallers can retrieve bucket ownership controls only through the blocking low-level executor, so owner-driven operation graphs cannot compose the read, preserve typed HTTP failure context, or reuse an established parent operation.
Solution: colocate bounded Ownership Controls reads with the Buckets provider
Add limited and operation-last provider forms, typed Finish and synchronous waiting over the same state machine, an exact prepared-request initiator, bounded same-response decoding, restart and operation-identity tests, and matching architecture and compatibility evidence.
07269323PutBucketOwnershipControls is available only through a blocking executor, so owner-driven callers cannot retain typed admission certainty or reconcile ambiguous publication without leaving the provider API.
Solution: add a non-replaying Ownership Controls mutation state machine
Colocate limited, operation-last, Finish, and synchronous forms in Client.Buckets; expose an exact low-level initiator; retain serialized bytes through terminal drain; classify only response-observed exact rejections as definitely not applied; and gate operation identity, restart, wire behavior, and the complete admission-certainty matrix.
33b28a12DeleteBucketOwnershipControls still uses the generic blocking configuration path, so composed callers cannot retain typed admission certainty, reuse an established operation, or reconcile an ambiguous deletion through the owning provider.
Solution: complete the provider-owned Ownership Controls trio
Add a non-replaying Delete operation with limited and operation-last forms, typed Finish and synchronous waiting; rewrite the existing convenience call through that state machine; expose exact low-level initiation; and gate operation identity, restart, wire behavior, and exhaustive certainty normalization.
da5c057fGetBucketEncryption is available only through the blocking executor, so owner-driven callers cannot compose its bounded response, cancellation, and deadline into an existing completion graph.
Solution: Add a provider-owned Get_Encryption operation
Add the exact low-level prepared initiator, limited and reusable provider overloads, typed Finish and synchronous wait over one bounded state machine. Cover exact-operation rejection, restart, response normalization, certainty, signed socket behavior, and document the expanded provider surface.
e00ca596DeleteBucketEncryption uses the blocking configuration dispatcher, so owner-driven callers cannot compose its one-shot mutation, deadline, cancellation, and admission certainty into an existing completion graph.
Solution: Add a provider-owned Delete_Encryption operation
Add the exact low-level initiator, limited and reusable provider overloads, typed Finish, and synchronous waits over one non-replaying state machine. Preserve the legacy Delete_Outcome surface through that same engine, remove its dead blocking branch, and cover exact binding, restart, certainty, response classes, and signed socket behavior.
179b16c4The five no-pin solve roots now select the published HTTP/QUIC 0.1.3-dev source that removes hook preprocessing, while CI and maintained prose still assert the previous flattened-client origin.
Solution: Advance the tracked HTTP origin qualification
Update CI and maintainer documentation to exact eb09a80, retain exact current-dev constraints and no external pins, and align nearby composable terminology with the provider-centric API.
179b16c4 → updated to 4401e0ecindex 4f7124b28 source commits
db16a412The existing CORS client can read and delete configurations, but callers cannot publish a complete configuration through the provider-owned operation model. They cannot retain typed admission certainty, restart an established component, or prove that the exact modeled request graph is covered without falling back to a separate blocking path.
Solution: add one provider-owned nonreplaying CORS mutation
Serialize the pinned flattened CORS graph within caller-selected XML limits, prepare and execute one-shot signed requests, and colocate limited, operation-last, typed Finish, and synchronous Set_CORS forms in Client.Buckets. Add reciprocal model ledgers, deterministic and signed socket corpora, certainty normalization, coverage accounting, GNATdoc, and maintainer qualification evidence.
df9d0d49GetBucketCors and DeleteBucketCors stop at synchronous execution even though their provider already owns exact prepared requests and bounded response decoding. Callers cannot reuse them inside completion-set state machines, and synchronous deletion does not share typed admission certainty with the composable client vocabulary.
Solution: colocate composable CORS reads and deletions with Client.Buckets
Add exact low-level initiators and provider-owned limited constructors, operation-last reusable procedures, typed Finish results, and synchronous waits on the same state machines. Preserve bounded decoding, non-replaying empty deletion bodies, admission certainty, and conservative outcome mapping, with signed socket, restart, mismatch, capacity, documentation, and normalization coverage.
b30f0812GetObjectLegalHold and PutObjectLegalHold stop at blocking low-level execution, forcing callers to leave provider-owned completion sets and obscuring mutation admission certainty.
Solution: colocate owner-driven Legal Hold operations in Client.Objects
Add exact prepared-request initiators, limited constructors, operation-last restart, typed Finish, shared synchronous waits, an owned nonreplayable mutation source, bounded response sinks, certainty normalization, signed socket and negative corpora, and maintainer documentation.
1160fd60GetObjectRetention and PutObjectRetention are available only through blocking client entry points, so completion-set state machines cannot compose exact-version retention reads or mutations without inventing a parallel wrapper. That obscures provider ownership and prevents typed admission certainty from flowing through caller-owned scheduling.
Solution: colocate Retention operations with Client.Objects
Add exact prepared-request initiators plus provider-owned limited constructors, operation-last reusable initiation, typed Finish, and synchronous waits driven by the same bounded state machines. Own the Put XML in a non-rewindable source, preserve conservative post-admission mutation certainty, require generation-bound read reconciliation, and gate restart, exact operation binding, normalization, and one-past response limits under native and lightweight owners.
aa0a3693GetObjectLockConfiguration and PutObjectLockConfiguration expose only blocking execution, so completion-set state machines cannot own their lifetime, drain, certainty, and bounded response handling through the bucket provider.
Solution: colocate owner-driven Object Lock configuration operations in Client.Buckets
Add exact Low_Level prepared-request initiators, limited constructors, operation-last reusable procedures, typed Finish results, and synchronous waits over the same state machines. Preserve bounded same-response reads, one-shot non-replaying mutation ownership, conservative admission certainty, and read-only reconciliation, with strict socket, certainty, documentation, and API gates.
d1dee5beCallers can prepare and execute bucket-encryption updates synchronously, but they cannot schedule the mutation beside other owner-driven operations without rebuilding its signing, body-lifetime, and publication-certainty rules.
Solution: add one nonreplaying provider-owned encryption mutation
Serialize the pinned presence-sensitive configuration into caller-bounded XML, retain its exact signed bytes in the parent operation, expose limited and operation-last Set_Encryption forms with typed Finish, and make the synchronous form wait on that same state machine. Add reciprocal model inventories, deterministic and signed-socket corpora, certainty normalization coverage, qualification evidence, and honest client-only compatibility claims.
0956dcd3The reviewed CORS, Object Lock configuration, Legal Hold, and Retention operation families live on a parallel lineage, while main independently adds PutBucketEncryption. Leaving the lineages separate prevents exact-tree qualification and publication of the complete provider-owned surface.
Solution: integrate both composable lineages without narrowing either contract
Retain every prepared initiator, provider-owned limited constructor, operation-last restart, typed Finish, synchronous wait, corpus, and qualification record from both sides. Reconcile the operation inventory at 52 provider operations and 49 prepared initiators while preserving the explicit three-call projection difference and all nonreplay, ownership, deadline, and certainty rules.
4401e0ecConfiguration-rejection cases can reach credential bootstrap before failing, which leaves a generated credentials file in the repository root and dirties an otherwise clean qualification tree.
Solution: isolate rejected-startup credentials in the test run directory
Give every expected-failure launch its own credentials path under RUN_ROOT so bootstrap artifacts are removed with the maintained test workspace.
4401e0ec → updated to 99706cceindex 91cc2b501 source commit
99706ccePutBucketCors, GetBucketCors, and DeleteBucketCors have complete clients and signed corpora, but authenticated servers cannot execute them and no backend retains their state. Deployments therefore cannot provide the full configuration lifecycle even though callers can construct every request.
Solution: complete bucket CORS across backends and server
Add the backend-neutral canonical-document contract and implement atomic memory, files, and SQLite persistence. Route authenticated server PUT, GET, and DELETE with strict query, owner, checksum, XML, and absence handling. Advance SQLite to schema 13, add migration, corruption, symlink, capacity, restart, and 132-case abrupt-crash evidence, update compatibility and coverage, and qualify the exact tree through full tests, server/provider gates, GNATdoc, and 936/936 SPARK checks.
99706cce → updated to 6ec20483index 4414c63a1 source commit
6ec20483The qualified client and ACL codec cannot read bucket or object ACLs from Flyology servers, even though the existing single-tenant mutation profile admits only private ownership. Raw subresource routing must also prevent encoded ACL queries from falling through to ordinary mutations.
Solution: Derive owner-only ACL responses in the S3 application
Route authenticated GetBucketAcl and version-selecting GetObjectAcl calls to backend existence reads, synthesize the exact CanonicalUser FULL_CONTROL policy from the stable tenant principal, and reject ACL mutations without publication. Add strict query, authorization, owner, payer, body, error-classification, encoded-subresource, in-process, and real-server socket gates; promote only the server coverage cells while leaving ACL persistence unclaimed.
0.1.0-devSQLite catalog backend for Flyology Object Storage
0761f9e2 → updated to aa799686index 16142be31 source commit
aa799686Strict Linux C compilation hides kill without an explicit POSIX feature contract, the proof project cannot locate an indirect Flyology project in Alire's shared cache, and shallow test checkouts cannot validate retained benchmark ancestry. The first public main CI therefore fails despite the locally qualified implementation.
Solution: make clean-checkout requirements explicit
Expose the POSIX.1-2008 declaration set locally in the durability bridge, prepend Alire's exported Flyology root only for maintained proof invocations, and fetch complete history for the test job that validates retained provenance.
aa799686 → updated to 98383e48index dee928e81 source commit
98383e48Linux SQLite consumers inherit an unnecessary libm link from the optional FTS5 module, while the proof gate imports Flyology through a transitive cache location that Alire does not expose consistently in clean hosted workspaces. This prevents the published development line from completing its maintained SQLite and SPARK gates.
Solution: narrow SQLite features and import the direct proof closure
Disable the unused FTS5 extension instead of adding a compensating linker dependency. Make the proof project import the direct HTTP and XML project dependencies, and let the maintained script invoke GNATprove directly without reconstructing a transitive cache path.
98383e48 → updated to 3455cde3index 8e99188e1 source commit
3455cde3A clean hosted proof job invokes Alire execution before dependency build roots and generated configuration exist. The resulting GPR search path names directories that have not been synchronized, so GNATprove cannot load the direct HTTP and XML project closure.
Solution: materialize the proof dependency closure before analysis
Run Alire through the pre-build stage before GNATprove. This synchronizes dependencies and runs configuration actions without compiling the Object Storage library; GNATprove remains the analyzer for the selected proof units.
3455cde3 → updated to 1978275bindex 27baef815 source commits
09e6eac2The bucket lifecycle client exposes deletion only through blocking execution, so callers cannot share a Completion_Set and HTTP owner while retaining typed admission certainty across a possibly admitted mutation.
Solution: Add one provider-owned non-replaying DeleteBucket operation
Add the limited constructor, operation-last reusable initiation, typed Finish, and synchronous wait on one state machine. Use a non-rewindable empty source, bounded response retention, conservative deletion certainty, signed socket and normalization corpora, and maintainer documentation.
22fec71cThe bucket provider exposes only blocking location discovery even though callers need the raw bounded response while coordinating related operations in one Completion_Set. They must either block outside their state machine or bypass the provider-owned certainty, cancellation, and response-decoding contract.
Solution: add one provider-owned bounded location read
Add limited-root and operation-last Get_Location forms, a typed Finish result, and the matching prepared low-level initiator. Make the typed synchronous and legacy convenience overloads wait on the same bounded owner-driven state machine, preserve established defaults and legacy region normalization, retain no borrowed request input after signing, and leave safe GET recovery to Flyology HTTP. Gate exact response/failure normalization, expected-owner signing, fragmented XML, restart, documentation, full deterministic tests, and the warning-strict 936-check proof campaign.
c0bae328Bucket-version reconciliation is available only through a blocking call, so structured callers cannot share a completion set, deadline, cancellation owner, or reusable operation while preserving the modeled response and HTTP failure evidence.
Solution: add one provider-owned bounded versioning read
Add limited-root and operation-last Get_Versioning forms with typed Finish in Client.Buckets, drive the prepared request through the shared HTTP state machine, retain XML under the versioning codec bound, preserve the established synchronous behavior, and qualify normalization, restart, documentation, and signed socket behavior.
39f97288Bucket-version updates are available only through blocking execution, so structured callers cannot own deadline, cancellation, and restart state or retain typed admission certainty for reconciliation.
Solution: add one provider-owned nonreplaying versioning mutation
Add limited-root and operation-last Set_Versioning_Configuration with typed Finish, move synchronous execution onto the same owned one-shot source, preserve exact certainty mapping, and qualify normalization, restart, documentation, signed sockets, and proof.
1978275bBucket-policy retrieval is available only through blocking execution, so structured callers cannot retain typed HTTP state, caller-selected response bounds, cancellation, and restart ownership in a larger operation graph.
Solution: add one provider-owned bounded policy read
Add limited-root and operation-last Get_Policy with typed Finish, route the synchronous parameter-record overload through the same state machine, enforce exact prepared-operation identity, and qualify normalization, bounds, restart, documentation, signed sockets, and proof.
1978275b → updated to 179b16c4index ade6ebbd9 source commits
0da1b4f7Callers can read bucket policies compositionally, but replacement and deletion still use separate blocking transport paths. That prevents provider-centric operation reuse, obscures admission certainty after ambiguous mutations, and leaves a socket-corpus startup failure able to strand clients before the server publishes its port.
Solution: add provider-owned nonreplaying policy mutations
Add limited Set_Policy and Delete_Policy roots, operation-last reusable initiation, typed Finish/results, exact low-level prepared-operation binding, and synchronous waits over the same one-shot state machines. Preserve owned payloads, caller-selected bounds, admission certainty, and read-only reconciliation rules; extend the signed corpus and make pre-publication server failure wake waiting clients deterministically.
5503d9d0The implementation manifest labels the Flyology files lane as not power durable even though its server uses the production durability policy. Campaign metadata also leaves that selected policy implicit, allowing later comparisons to overstate or misclassify durability equivalence.
Solution: Pin and retain the production durability selection
Label the matrix lane power-loss-durable, pass Power_Loss_Durable explicitly to the files server, record the exact commit policy in both retained matrix metadata streams, and gate the metadata field. Clarify the guide while leaving immutable historical evidence unchanged.
3453828cPutPublicAccessBlock, GetPublicAccessBlock, and DeletePublicAccessBlock are available through synchronous helpers but not through the provider-owned operation model. Callers cannot compose these security controls with their completion sets, and mutation failures lack the typed certainty needed to choose read-only reconciliation without replay.
Solution: Add provider-owned public access block operations
Add limited constructors, operation-last restart procedures, typed Finish results, and synchronous waits on the same Client.Buckets state machines. Keep mutation bodies non-rewindable, preserve admission certainty and bounded same-response reads, reject mismatched prepared operations before HTTP admission, and cover lifecycle, restart, certainty, and documentation contracts.
551d514dCallers can retrieve bucket ownership controls only through the blocking low-level executor, so owner-driven operation graphs cannot compose the read, preserve typed HTTP failure context, or reuse an established parent operation.
Solution: colocate bounded Ownership Controls reads with the Buckets provider
Add limited and operation-last provider forms, typed Finish and synchronous waiting over the same state machine, an exact prepared-request initiator, bounded same-response decoding, restart and operation-identity tests, and matching architecture and compatibility evidence.
07269323PutBucketOwnershipControls is available only through a blocking executor, so owner-driven callers cannot retain typed admission certainty or reconcile ambiguous publication without leaving the provider API.
Solution: add a non-replaying Ownership Controls mutation state machine
Colocate limited, operation-last, Finish, and synchronous forms in Client.Buckets; expose an exact low-level initiator; retain serialized bytes through terminal drain; classify only response-observed exact rejections as definitely not applied; and gate operation identity, restart, wire behavior, and the complete admission-certainty matrix.
33b28a12DeleteBucketOwnershipControls still uses the generic blocking configuration path, so composed callers cannot retain typed admission certainty, reuse an established operation, or reconcile an ambiguous deletion through the owning provider.
Solution: complete the provider-owned Ownership Controls trio
Add a non-replaying Delete operation with limited and operation-last forms, typed Finish and synchronous waiting; rewrite the existing convenience call through that state machine; expose exact low-level initiation; and gate operation identity, restart, wire behavior, and exhaustive certainty normalization.
da5c057fGetBucketEncryption is available only through the blocking executor, so owner-driven callers cannot compose its bounded response, cancellation, and deadline into an existing completion graph.
Solution: Add a provider-owned Get_Encryption operation
Add the exact low-level prepared initiator, limited and reusable provider overloads, typed Finish and synchronous wait over one bounded state machine. Cover exact-operation rejection, restart, response normalization, certainty, signed socket behavior, and document the expanded provider surface.
e00ca596DeleteBucketEncryption uses the blocking configuration dispatcher, so owner-driven callers cannot compose its one-shot mutation, deadline, cancellation, and admission certainty into an existing completion graph.
Solution: Add a provider-owned Delete_Encryption operation
Add the exact low-level initiator, limited and reusable provider overloads, typed Finish, and synchronous waits over one non-replaying state machine. Preserve the legacy Delete_Outcome surface through that same engine, remove its dead blocking branch, and cover exact binding, restart, certainty, response classes, and signed socket behavior.
179b16c4The five no-pin solve roots now select the published HTTP/QUIC 0.1.3-dev source that removes hook preprocessing, while CI and maintained prose still assert the previous flattened-client origin.
Solution: Advance the tracked HTTP origin qualification
Update CI and maintainer documentation to exact eb09a80, retain exact current-dev constraints and no external pins, and align nearby composable terminology with the provider-centric API.
179b16c4 → updated to 4401e0ecindex 4f7124b28 source commits
db16a412The existing CORS client can read and delete configurations, but callers cannot publish a complete configuration through the provider-owned operation model. They cannot retain typed admission certainty, restart an established component, or prove that the exact modeled request graph is covered without falling back to a separate blocking path.
Solution: add one provider-owned nonreplaying CORS mutation
Serialize the pinned flattened CORS graph within caller-selected XML limits, prepare and execute one-shot signed requests, and colocate limited, operation-last, typed Finish, and synchronous Set_CORS forms in Client.Buckets. Add reciprocal model ledgers, deterministic and signed socket corpora, certainty normalization, coverage accounting, GNATdoc, and maintainer qualification evidence.
df9d0d49GetBucketCors and DeleteBucketCors stop at synchronous execution even though their provider already owns exact prepared requests and bounded response decoding. Callers cannot reuse them inside completion-set state machines, and synchronous deletion does not share typed admission certainty with the composable client vocabulary.
Solution: colocate composable CORS reads and deletions with Client.Buckets
Add exact low-level initiators and provider-owned limited constructors, operation-last reusable procedures, typed Finish results, and synchronous waits on the same state machines. Preserve bounded decoding, non-replaying empty deletion bodies, admission certainty, and conservative outcome mapping, with signed socket, restart, mismatch, capacity, documentation, and normalization coverage.
b30f0812GetObjectLegalHold and PutObjectLegalHold stop at blocking low-level execution, forcing callers to leave provider-owned completion sets and obscuring mutation admission certainty.
Solution: colocate owner-driven Legal Hold operations in Client.Objects
Add exact prepared-request initiators, limited constructors, operation-last restart, typed Finish, shared synchronous waits, an owned nonreplayable mutation source, bounded response sinks, certainty normalization, signed socket and negative corpora, and maintainer documentation.
1160fd60GetObjectRetention and PutObjectRetention are available only through blocking client entry points, so completion-set state machines cannot compose exact-version retention reads or mutations without inventing a parallel wrapper. That obscures provider ownership and prevents typed admission certainty from flowing through caller-owned scheduling.
Solution: colocate Retention operations with Client.Objects
Add exact prepared-request initiators plus provider-owned limited constructors, operation-last reusable initiation, typed Finish, and synchronous waits driven by the same bounded state machines. Own the Put XML in a non-rewindable source, preserve conservative post-admission mutation certainty, require generation-bound read reconciliation, and gate restart, exact operation binding, normalization, and one-past response limits under native and lightweight owners.
aa0a3693GetObjectLockConfiguration and PutObjectLockConfiguration expose only blocking execution, so completion-set state machines cannot own their lifetime, drain, certainty, and bounded response handling through the bucket provider.
Solution: colocate owner-driven Object Lock configuration operations in Client.Buckets
Add exact Low_Level prepared-request initiators, limited constructors, operation-last reusable procedures, typed Finish results, and synchronous waits over the same state machines. Preserve bounded same-response reads, one-shot non-replaying mutation ownership, conservative admission certainty, and read-only reconciliation, with strict socket, certainty, documentation, and API gates.
d1dee5beCallers can prepare and execute bucket-encryption updates synchronously, but they cannot schedule the mutation beside other owner-driven operations without rebuilding its signing, body-lifetime, and publication-certainty rules.
Solution: add one nonreplaying provider-owned encryption mutation
Serialize the pinned presence-sensitive configuration into caller-bounded XML, retain its exact signed bytes in the parent operation, expose limited and operation-last Set_Encryption forms with typed Finish, and make the synchronous form wait on that same state machine. Add reciprocal model inventories, deterministic and signed-socket corpora, certainty normalization coverage, qualification evidence, and honest client-only compatibility claims.
0956dcd3The reviewed CORS, Object Lock configuration, Legal Hold, and Retention operation families live on a parallel lineage, while main independently adds PutBucketEncryption. Leaving the lineages separate prevents exact-tree qualification and publication of the complete provider-owned surface.
Solution: integrate both composable lineages without narrowing either contract
Retain every prepared initiator, provider-owned limited constructor, operation-last restart, typed Finish, synchronous wait, corpus, and qualification record from both sides. Reconcile the operation inventory at 52 provider operations and 49 prepared initiators while preserving the explicit three-call projection difference and all nonreplay, ownership, deadline, and certainty rules.
4401e0ecConfiguration-rejection cases can reach credential bootstrap before failing, which leaves a generated credentials file in the repository root and dirties an otherwise clean qualification tree.
Solution: isolate rejected-startup credentials in the test run directory
Give every expected-failure launch its own credentials path under RUN_ROOT so bootstrap artifacts are removed with the maintained test workspace.
4401e0ec → updated to 99706cceindex 91cc2b501 source commit
99706ccePutBucketCors, GetBucketCors, and DeleteBucketCors have complete clients and signed corpora, but authenticated servers cannot execute them and no backend retains their state. Deployments therefore cannot provide the full configuration lifecycle even though callers can construct every request.
Solution: complete bucket CORS across backends and server
Add the backend-neutral canonical-document contract and implement atomic memory, files, and SQLite persistence. Route authenticated server PUT, GET, and DELETE with strict query, owner, checksum, XML, and absence handling. Advance SQLite to schema 13, add migration, corruption, symlink, capacity, restart, and 132-case abrupt-crash evidence, update compatibility and coverage, and qualify the exact tree through full tests, server/provider gates, GNATdoc, and 936/936 SPARK checks.
99706cce → updated to 6ec20483index 4414c63a1 source commit
6ec20483The qualified client and ACL codec cannot read bucket or object ACLs from Flyology servers, even though the existing single-tenant mutation profile admits only private ownership. Raw subresource routing must also prevent encoded ACL queries from falling through to ordinary mutations.
Solution: Derive owner-only ACL responses in the S3 application
Route authenticated GetBucketAcl and version-selecting GetObjectAcl calls to backend existence reads, synthesize the exact CanonicalUser FULL_CONTROL policy from the stable tenant principal, and reject ACL mutations without publication. Add strict query, authorization, owner, payer, body, error-classification, encoded-subresource, in-process, and real-server socket gates; promote only the server coverage cells while leaving ACL persistence unclaimed.
56939882 on GitHub
0.1.0-devIncremental bounded JSON parsing and writing for Ada
gnat >=13 & <178b8fdf03 on GitHub
0.1.3-devHTTP client and server library for Flyology tasks
0.1.1-devAllocation-conscious URI, IRI, and URL reference parser
0.1.3-devAda-native QUIC transport for Flyology tasks
b454736c on GitHub
0.1.0-devS3 client and pluggable object-storage server for Flyology
flyology_http =0.1.3-devxmlada ^26.0.00.1.0-devSupervised S3 server and management console for Flyology Object Storage
flyology =0.1.1-devflyology_http =0.1.3-devflyology_object_storage =0.1.0-devflyology_object_storage_sqlite =0.1.0-dev0.1.0-devSQLite catalog backend for Flyology Object Storage
flyology_object_storage =0.1.0-dev0.1.1-devExperimental GNAT runtime extension for native and lightweight Ada tasks
243833e6 → updated to 810f1088index 36533e238 source commits
86f831ccA slot whose nonwrapping generation reaches its maximum is released back into the free stack. Its next acquisition fails after the pool has already removed it, corrupting availability and potentially leaving waiters with inconsistent accounting. The remoting buffer-domain work depends on generation-safe capability reuse.
Solution: Retire slots after their final published generation
Preflight generation exhaustion before mutating allocation state, omit final-generation slots from the free stack on release, document permanent retirement, and add a statically elided test seam that exercises near-wrap behavior. The regression covers surviving capacity, complete retirement, nonblocking and timed acquisition, coverage integration, and production hook-symbol elision.
06bbc9c0Remoting sessions need to choose among heterogeneous buffer pools while moving payload ownership through definite transport capabilities. A pool-bound buffer alone either exposes an access value across the driver boundary or loses the lifetime, domain, and exception-atomicity guarantees needed for zero-copy delivery.
Solution: Add domain-bound heterogeneous buffer capabilities
Introduce an immutable buffer-domain catalogue with opaque pool references and access-free driver capabilities. Ownership transfers use abort-deferred protected commits, domain-bound public owners retain compile-time lifetime checks, and all storage access validates the supplied domain. Add wrong-domain, zero-copy, finalization, rollback, hook-elision, and compile-negative lifetime coverage.
50b84004A remoting session needs exclusive ownership of one heterogeneous buffer pool while messages are in flight, but the existing domain admitted only ordinary acquisitions. It had no bounded reservation claims, generation-safe delayed release, or authority token for acknowledging a retired session after its final context-state cut. Base buffer acquisition and release also published ownership through protected-call copy-out boundaries that could lose recovery authority under abort.
Solution: Add bounded generation-stamped buffer pool reservations
Add controlled reservation claims and authorized release tokens, exact reservation provenance on buffers and capabilities, total acquisition statuses, nonwrapping generations, pending-release reissue, and permanent exhaustion. Publish and clear actual ownership tokens inside protected actions, retain abort-safe cleanup guards, document lock and lifecycle rules, and cover failure boundaries, provenance, finalization, status precedence, lifetime rejection, and production hook elision.
a4ab6d4bA remoting context must publish a reserved header and an ordinary payload as one abort-deferred ownership transition. The validated buffer operations re-enter the domain gate, so calling them while the context registry is protected would invert the lock boundary and leave no safe compound commit point.
Solution: Add prevalidated direct capability commit moves
Add provider-only limited ownership moves whose bodies contain only same-subtype field transfers and source clearing. Document the caller-owned validation and publication cut, cover heterogeneous pool provenance, tags, bytes, rejection, and abort behavior, and make the root suite fail closed on any unreviewed helper instruction or retained test hook.
f387f8ecThe disabled test-hook variant necessarily contributes its package elaboration and literal Enabled symbols to the production archive. The behavioral suite matches the package prefix broadly, so a clean production build fails before the repository can verify the new buffer-domain boundary.
Solution: Match only executable buffer test seams
Restrict the archive classifier to the arm, consume, note, and live hook operations plus the imported disabled sentinels. This keeps surviving test behavior detectable without treating the inert disabled package metadata as production test code.
c9125bedThe new domain catalogue, sentinels, acquisition results, reservation overloads, and private controlled-operation helpers lack complete GNATdoc annotations. Strict API generation therefore rejects the reviewed dependency series and cannot publish its public contract.
Solution: Complete buffer domain API annotations
Document every new public entity and result literal, describe each reservation overload formal, and exclude private implementation helpers while documenting their formals as required by GNATdoc. Format the specification with the owning Flyology project settings.
5c3d43c4The generated API describes Reservation_Not_Active as never published even though a previously published exact generation can later become inactive. It also attributes the bounded claim capacity to a reservation although ordinary unreserved acquisitions share the same pool claim bound.
Solution: Describe status outcomes by current pool state
Define Reservation_Not_Active in terms of the exact generation currently being inactive and attribute Claim_Limit_Reached to the pool claim capacity. The wording now matches the implemented and tested status paths.
810f1088Add abort-safe heterogeneous buffer domains for remoting
810f1088 → updated to 3859f412index ba75376c2 source commits
84b425b1The out-of-tree consumer does not reference the buffer-domain packages. A stale exact-version Alire lock can therefore supply a pre-domain Flyology source tree while the consumer still builds, leaving the published dependency surface unverified.
Solution: Compile the buffer-domain API out of tree
Make the external consumer import Domains and Domains.Drivers, acquire and release a domain-owned buffer, and call the driver diagnostic so the two package specifications and bodies must be packaged, compiled, and linked.
3859f412Verify buffer-domain API from an external consumer
0.1.0-devCaller-owned memory allocation algorithms for Ada
243833e6 → updated to 810f1088index 36533e238 source commits
86f831ccA slot whose nonwrapping generation reaches its maximum is released back into the free stack. Its next acquisition fails after the pool has already removed it, corrupting availability and potentially leaving waiters with inconsistent accounting. The remoting buffer-domain work depends on generation-safe capability reuse.
Solution: Retire slots after their final published generation
Preflight generation exhaustion before mutating allocation state, omit final-generation slots from the free stack on release, document permanent retirement, and add a statically elided test seam that exercises near-wrap behavior. The regression covers surviving capacity, complete retirement, nonblocking and timed acquisition, coverage integration, and production hook-symbol elision.
06bbc9c0Remoting sessions need to choose among heterogeneous buffer pools while moving payload ownership through definite transport capabilities. A pool-bound buffer alone either exposes an access value across the driver boundary or loses the lifetime, domain, and exception-atomicity guarantees needed for zero-copy delivery.
Solution: Add domain-bound heterogeneous buffer capabilities
Introduce an immutable buffer-domain catalogue with opaque pool references and access-free driver capabilities. Ownership transfers use abort-deferred protected commits, domain-bound public owners retain compile-time lifetime checks, and all storage access validates the supplied domain. Add wrong-domain, zero-copy, finalization, rollback, hook-elision, and compile-negative lifetime coverage.
50b84004A remoting session needs exclusive ownership of one heterogeneous buffer pool while messages are in flight, but the existing domain admitted only ordinary acquisitions. It had no bounded reservation claims, generation-safe delayed release, or authority token for acknowledging a retired session after its final context-state cut. Base buffer acquisition and release also published ownership through protected-call copy-out boundaries that could lose recovery authority under abort.
Solution: Add bounded generation-stamped buffer pool reservations
Add controlled reservation claims and authorized release tokens, exact reservation provenance on buffers and capabilities, total acquisition statuses, nonwrapping generations, pending-release reissue, and permanent exhaustion. Publish and clear actual ownership tokens inside protected actions, retain abort-safe cleanup guards, document lock and lifecycle rules, and cover failure boundaries, provenance, finalization, status precedence, lifetime rejection, and production hook elision.
a4ab6d4bA remoting context must publish a reserved header and an ordinary payload as one abort-deferred ownership transition. The validated buffer operations re-enter the domain gate, so calling them while the context registry is protected would invert the lock boundary and leave no safe compound commit point.
Solution: Add prevalidated direct capability commit moves
Add provider-only limited ownership moves whose bodies contain only same-subtype field transfers and source clearing. Document the caller-owned validation and publication cut, cover heterogeneous pool provenance, tags, bytes, rejection, and abort behavior, and make the root suite fail closed on any unreviewed helper instruction or retained test hook.
f387f8ecThe disabled test-hook variant necessarily contributes its package elaboration and literal Enabled symbols to the production archive. The behavioral suite matches the package prefix broadly, so a clean production build fails before the repository can verify the new buffer-domain boundary.
Solution: Match only executable buffer test seams
Restrict the archive classifier to the arm, consume, note, and live hook operations plus the imported disabled sentinels. This keeps surviving test behavior detectable without treating the inert disabled package metadata as production test code.
c9125bedThe new domain catalogue, sentinels, acquisition results, reservation overloads, and private controlled-operation helpers lack complete GNATdoc annotations. Strict API generation therefore rejects the reviewed dependency series and cannot publish its public contract.
Solution: Complete buffer domain API annotations
Document every new public entity and result literal, describe each reservation overload formal, and exclude private implementation helpers while documenting their formals as required by GNATdoc. Format the specification with the owning Flyology project settings.
5c3d43c4The generated API describes Reservation_Not_Active as never published even though a previously published exact generation can later become inactive. It also attributes the bounded claim capacity to a reservation although ordinary unreserved acquisitions share the same pool claim bound.
Solution: Describe status outcomes by current pool state
Define Reservation_Not_Active in terms of the exact generation currently being inactive and attribute Claim_Limit_Reached to the pool claim capacity. The wording now matches the implemented and tested status paths.
810f1088Add abort-safe heterogeneous buffer domains for remoting
810f1088 → updated to 3859f412index ba75376c2 source commits
84b425b1The out-of-tree consumer does not reference the buffer-domain packages. A stale exact-version Alire lock can therefore supply a pre-domain Flyology source tree while the consumer still builds, leaving the published dependency surface unverified.
Solution: Compile the buffer-domain API out of tree
Make the external consumer import Domains and Domains.Drivers, acquire and release a domain-owned buffer, and call the driver diagnostic so the two package specifications and bodies must be packaged, compiled, and linked.
3859f412Verify buffer-domain API from an external consumer
0.1.1-devAdaptive nanosecond-scale benchmarking for Ada
243833e6 → updated to 810f1088index 36533e238 source commits
86f831ccA slot whose nonwrapping generation reaches its maximum is released back into the free stack. Its next acquisition fails after the pool has already removed it, corrupting availability and potentially leaving waiters with inconsistent accounting. The remoting buffer-domain work depends on generation-safe capability reuse.
Solution: Retire slots after their final published generation
Preflight generation exhaustion before mutating allocation state, omit final-generation slots from the free stack on release, document permanent retirement, and add a statically elided test seam that exercises near-wrap behavior. The regression covers surviving capacity, complete retirement, nonblocking and timed acquisition, coverage integration, and production hook-symbol elision.
06bbc9c0Remoting sessions need to choose among heterogeneous buffer pools while moving payload ownership through definite transport capabilities. A pool-bound buffer alone either exposes an access value across the driver boundary or loses the lifetime, domain, and exception-atomicity guarantees needed for zero-copy delivery.
Solution: Add domain-bound heterogeneous buffer capabilities
Introduce an immutable buffer-domain catalogue with opaque pool references and access-free driver capabilities. Ownership transfers use abort-deferred protected commits, domain-bound public owners retain compile-time lifetime checks, and all storage access validates the supplied domain. Add wrong-domain, zero-copy, finalization, rollback, hook-elision, and compile-negative lifetime coverage.
50b84004A remoting session needs exclusive ownership of one heterogeneous buffer pool while messages are in flight, but the existing domain admitted only ordinary acquisitions. It had no bounded reservation claims, generation-safe delayed release, or authority token for acknowledging a retired session after its final context-state cut. Base buffer acquisition and release also published ownership through protected-call copy-out boundaries that could lose recovery authority under abort.
Solution: Add bounded generation-stamped buffer pool reservations
Add controlled reservation claims and authorized release tokens, exact reservation provenance on buffers and capabilities, total acquisition statuses, nonwrapping generations, pending-release reissue, and permanent exhaustion. Publish and clear actual ownership tokens inside protected actions, retain abort-safe cleanup guards, document lock and lifecycle rules, and cover failure boundaries, provenance, finalization, status precedence, lifetime rejection, and production hook elision.
a4ab6d4bA remoting context must publish a reserved header and an ordinary payload as one abort-deferred ownership transition. The validated buffer operations re-enter the domain gate, so calling them while the context registry is protected would invert the lock boundary and leave no safe compound commit point.
Solution: Add prevalidated direct capability commit moves
Add provider-only limited ownership moves whose bodies contain only same-subtype field transfers and source clearing. Document the caller-owned validation and publication cut, cover heterogeneous pool provenance, tags, bytes, rejection, and abort behavior, and make the root suite fail closed on any unreviewed helper instruction or retained test hook.
f387f8ecThe disabled test-hook variant necessarily contributes its package elaboration and literal Enabled symbols to the production archive. The behavioral suite matches the package prefix broadly, so a clean production build fails before the repository can verify the new buffer-domain boundary.
Solution: Match only executable buffer test seams
Restrict the archive classifier to the arm, consume, note, and live hook operations plus the imported disabled sentinels. This keeps surviving test behavior detectable without treating the inert disabled package metadata as production test code.
c9125bedThe new domain catalogue, sentinels, acquisition results, reservation overloads, and private controlled-operation helpers lack complete GNATdoc annotations. Strict API generation therefore rejects the reviewed dependency series and cannot publish its public contract.
Solution: Complete buffer domain API annotations
Document every new public entity and result literal, describe each reservation overload formal, and exclude private implementation helpers while documenting their formals as required by GNATdoc. Format the specification with the owning Flyology project settings.
5c3d43c4The generated API describes Reservation_Not_Active as never published even though a previously published exact generation can later become inactive. It also attributes the bounded claim capacity to a reservation although ordinary unreserved acquisitions share the same pool claim bound.
Solution: Describe status outcomes by current pool state
Define Reservation_Not_Active in terms of the exact generation currently being inactive and attribute Claim_Limit_Reached to the pool claim capacity. The wording now matches the implemented and tested status paths.
810f1088Add abort-safe heterogeneous buffer domains for remoting
810f1088 → updated to 3859f412index ba75376c2 source commits
84b425b1The out-of-tree consumer does not reference the buffer-domain packages. A stale exact-version Alire lock can therefore supply a pre-domain Flyology source tree while the consumer still builds, leaving the published dependency surface unverified.
Solution: Compile the buffer-domain API out of tree
Make the external consumer import Domains and Domains.Drivers, acquire and release a domain-owned buffer, and call the driver diagnostic so the two package specifications and bodies must be packaged, compiled, and linked.
3859f412Verify buffer-domain API from an external consumer
0.1.1-devCache-line-aware storage and cache information for Ada
243833e6 → updated to 810f1088index 36533e238 source commits
86f831ccA slot whose nonwrapping generation reaches its maximum is released back into the free stack. Its next acquisition fails after the pool has already removed it, corrupting availability and potentially leaving waiters with inconsistent accounting. The remoting buffer-domain work depends on generation-safe capability reuse.
Solution: Retire slots after their final published generation
Preflight generation exhaustion before mutating allocation state, omit final-generation slots from the free stack on release, document permanent retirement, and add a statically elided test seam that exercises near-wrap behavior. The regression covers surviving capacity, complete retirement, nonblocking and timed acquisition, coverage integration, and production hook-symbol elision.
06bbc9c0Remoting sessions need to choose among heterogeneous buffer pools while moving payload ownership through definite transport capabilities. A pool-bound buffer alone either exposes an access value across the driver boundary or loses the lifetime, domain, and exception-atomicity guarantees needed for zero-copy delivery.
Solution: Add domain-bound heterogeneous buffer capabilities
Introduce an immutable buffer-domain catalogue with opaque pool references and access-free driver capabilities. Ownership transfers use abort-deferred protected commits, domain-bound public owners retain compile-time lifetime checks, and all storage access validates the supplied domain. Add wrong-domain, zero-copy, finalization, rollback, hook-elision, and compile-negative lifetime coverage.
50b84004A remoting session needs exclusive ownership of one heterogeneous buffer pool while messages are in flight, but the existing domain admitted only ordinary acquisitions. It had no bounded reservation claims, generation-safe delayed release, or authority token for acknowledging a retired session after its final context-state cut. Base buffer acquisition and release also published ownership through protected-call copy-out boundaries that could lose recovery authority under abort.
Solution: Add bounded generation-stamped buffer pool reservations
Add controlled reservation claims and authorized release tokens, exact reservation provenance on buffers and capabilities, total acquisition statuses, nonwrapping generations, pending-release reissue, and permanent exhaustion. Publish and clear actual ownership tokens inside protected actions, retain abort-safe cleanup guards, document lock and lifecycle rules, and cover failure boundaries, provenance, finalization, status precedence, lifetime rejection, and production hook elision.
a4ab6d4bA remoting context must publish a reserved header and an ordinary payload as one abort-deferred ownership transition. The validated buffer operations re-enter the domain gate, so calling them while the context registry is protected would invert the lock boundary and leave no safe compound commit point.
Solution: Add prevalidated direct capability commit moves
Add provider-only limited ownership moves whose bodies contain only same-subtype field transfers and source clearing. Document the caller-owned validation and publication cut, cover heterogeneous pool provenance, tags, bytes, rejection, and abort behavior, and make the root suite fail closed on any unreviewed helper instruction or retained test hook.
f387f8ecThe disabled test-hook variant necessarily contributes its package elaboration and literal Enabled symbols to the production archive. The behavioral suite matches the package prefix broadly, so a clean production build fails before the repository can verify the new buffer-domain boundary.
Solution: Match only executable buffer test seams
Restrict the archive classifier to the arm, consume, note, and live hook operations plus the imported disabled sentinels. This keeps surviving test behavior detectable without treating the inert disabled package metadata as production test code.
c9125bedThe new domain catalogue, sentinels, acquisition results, reservation overloads, and private controlled-operation helpers lack complete GNATdoc annotations. Strict API generation therefore rejects the reviewed dependency series and cannot publish its public contract.
Solution: Complete buffer domain API annotations
Document every new public entity and result literal, describe each reservation overload formal, and exclude private implementation helpers while documenting their formals as required by GNATdoc. Format the specification with the owning Flyology project settings.
5c3d43c4The generated API describes Reservation_Not_Active as never published even though a previously published exact generation can later become inactive. It also attributes the bounded claim capacity to a reservation although ordinary unreserved acquisitions share the same pool claim bound.
Solution: Describe status outcomes by current pool state
Define Reservation_Not_Active in terms of the exact generation currently being inactive and attribute Claim_Limit_Reached to the pool claim capacity. The wording now matches the implemented and tested status paths.
810f1088Add abort-safe heterogeneous buffer domains for remoting
810f1088 → updated to 3859f412index ba75376c2 source commits
84b425b1The out-of-tree consumer does not reference the buffer-domain packages. A stale exact-version Alire lock can therefore supply a pre-domain Flyology source tree while the consumer still builds, leaving the published dependency surface unverified.
Solution: Compile the buffer-domain API out of tree
Make the external consumer import Domains and Domains.Drivers, acquire and release a domain-owned buffer, and call the driver diagnostic so the two package specifications and bodies must be packaged, compiled, and linked.
3859f412Verify buffer-domain API from an external consumer
0.1.1-devBounded in-memory tracing and gauges for Ada
243833e6 → updated to 810f1088index 36533e238 source commits
86f831ccA slot whose nonwrapping generation reaches its maximum is released back into the free stack. Its next acquisition fails after the pool has already removed it, corrupting availability and potentially leaving waiters with inconsistent accounting. The remoting buffer-domain work depends on generation-safe capability reuse.
Solution: Retire slots after their final published generation
Preflight generation exhaustion before mutating allocation state, omit final-generation slots from the free stack on release, document permanent retirement, and add a statically elided test seam that exercises near-wrap behavior. The regression covers surviving capacity, complete retirement, nonblocking and timed acquisition, coverage integration, and production hook-symbol elision.
06bbc9c0Remoting sessions need to choose among heterogeneous buffer pools while moving payload ownership through definite transport capabilities. A pool-bound buffer alone either exposes an access value across the driver boundary or loses the lifetime, domain, and exception-atomicity guarantees needed for zero-copy delivery.
Solution: Add domain-bound heterogeneous buffer capabilities
Introduce an immutable buffer-domain catalogue with opaque pool references and access-free driver capabilities. Ownership transfers use abort-deferred protected commits, domain-bound public owners retain compile-time lifetime checks, and all storage access validates the supplied domain. Add wrong-domain, zero-copy, finalization, rollback, hook-elision, and compile-negative lifetime coverage.
50b84004A remoting session needs exclusive ownership of one heterogeneous buffer pool while messages are in flight, but the existing domain admitted only ordinary acquisitions. It had no bounded reservation claims, generation-safe delayed release, or authority token for acknowledging a retired session after its final context-state cut. Base buffer acquisition and release also published ownership through protected-call copy-out boundaries that could lose recovery authority under abort.
Solution: Add bounded generation-stamped buffer pool reservations
Add controlled reservation claims and authorized release tokens, exact reservation provenance on buffers and capabilities, total acquisition statuses, nonwrapping generations, pending-release reissue, and permanent exhaustion. Publish and clear actual ownership tokens inside protected actions, retain abort-safe cleanup guards, document lock and lifecycle rules, and cover failure boundaries, provenance, finalization, status precedence, lifetime rejection, and production hook elision.
a4ab6d4bA remoting context must publish a reserved header and an ordinary payload as one abort-deferred ownership transition. The validated buffer operations re-enter the domain gate, so calling them while the context registry is protected would invert the lock boundary and leave no safe compound commit point.
Solution: Add prevalidated direct capability commit moves
Add provider-only limited ownership moves whose bodies contain only same-subtype field transfers and source clearing. Document the caller-owned validation and publication cut, cover heterogeneous pool provenance, tags, bytes, rejection, and abort behavior, and make the root suite fail closed on any unreviewed helper instruction or retained test hook.
f387f8ecThe disabled test-hook variant necessarily contributes its package elaboration and literal Enabled symbols to the production archive. The behavioral suite matches the package prefix broadly, so a clean production build fails before the repository can verify the new buffer-domain boundary.
Solution: Match only executable buffer test seams
Restrict the archive classifier to the arm, consume, note, and live hook operations plus the imported disabled sentinels. This keeps surviving test behavior detectable without treating the inert disabled package metadata as production test code.
c9125bedThe new domain catalogue, sentinels, acquisition results, reservation overloads, and private controlled-operation helpers lack complete GNATdoc annotations. Strict API generation therefore rejects the reviewed dependency series and cannot publish its public contract.
Solution: Complete buffer domain API annotations
Document every new public entity and result literal, describe each reservation overload formal, and exclude private implementation helpers while documenting their formals as required by GNATdoc. Format the specification with the owning Flyology project settings.
5c3d43c4The generated API describes Reservation_Not_Active as never published even though a previously published exact generation can later become inactive. It also attributes the bounded claim capacity to a reservation although ordinary unreserved acquisitions share the same pool claim bound.
Solution: Describe status outcomes by current pool state
Define Reservation_Not_Active in terms of the exact generation currently being inactive and attribute Claim_Limit_Reached to the pool claim capacity. The wording now matches the implemented and tested status paths.
810f1088Add abort-safe heterogeneous buffer domains for remoting
810f1088 → updated to 3859f412index ba75376c2 source commits
84b425b1The out-of-tree consumer does not reference the buffer-domain packages. A stale exact-version Alire lock can therefore supply a pre-domain Flyology source tree while the consumer still builds, leaving the published dependency surface unverified.
Solution: Compile the buffer-domain API out of tree
Make the external consumer import Domains and Domains.Drivers, acquire and release a domain-owned buffer, and call the driver diagnostic so the two package specifications and bodies must be packaged, compiled, and linked.
3859f412Verify buffer-domain API from an external consumer
0.1.3-devHTTP client and server library for Flyology tasks
a65f24f4 → updated to 537edbc5index 519c45e43 source commits
f1e65c3dA server can return a complete response without Connection: close and then silently close the reusable HTTP/1 transport. The next public bounded-buffer GET or HEAD fails at response-head parsing even though the synchronous adapter already performs guarded one-shot stale recovery. This prevents object-storage reconciliation after a conclusive conditional response.
Solution: Extend guarded stale recovery to complete-body buffer exchanges
Allow public buffer exchanges to use the existing safe-method, no-source, no-response-byte, reused-transport retry path. Add a deterministic native/lightweight established-child regression that models the complete 412 followed by a dropped GET and replacement connection, and record the executable scenario in the maintained corpus.
1cded286The HTTP test action invokes IRI and QUIC as nested Alire projects. After the development index selected a newer compiler, exported GNAT and GPRbuild prefixes from the outer solve contaminated the nested solve, and the QUIC lane maintained a separate incomplete unset list.
Solution: Centralize nested toolchain environment isolation
Clear Flyology, compiler, GPRbuild, and project-path prefixes in the shared nested-test helper, route both IRI and QUIC through it, and extend the action-context regression to cover the toolchain variables.
537edbc5Recover scoped safe reads from silently stale H1 leases
537edbc5 → updated to b5cd966dindex e812215c3 source commits
22d8ee67Scoped describes an operation's limited lifetime and ownership discipline, not the provider that performs it. Keeping client and server operation forms in child namespaces makes callers learn two API trees for one state machine, obscures synchronous parity, and encourages downstream wrappers and documentation drift.
Solution: Colocate composable overloads with their providers
Move the limited constructors, same-name established initiators, operation state accessors, and typed Finish overloads directly into Flyology.HTTP.Client and Flyology.HTTP.Server. Remove both Scoped namespaces without aliases, migrate every executable caller, and retain the existing ownership, certainty, cancellation, drain, deadline, protocol, and guarded stale-H1 behavior.
17a403e5The corpus vocabulary, differential tools, generated expectations, architecture notes, and website guides still name Scoped as a parallel API style. That language would teach the removed namespace and leave provider ownership hard to discover after the intentional development-line source break.
Solution: Document provider-centric composable operations
Rename corpus execution styles to composable buffer and sink forms, regenerate the Ada oracle, and update conformance and differential tooling. Revise the guides and architecture to place root constructors, established initiation, and typed Finish beside synchronous provider calls while preserving scoped lifetime as an operation semantic.
b5cd966dColocate composable HTTP operations with providers
0.1.1-devAllocation-conscious URI, IRI, and URL reference parser
a65f24f4 → updated to 537edbc5index 519c45e43 source commits
f1e65c3dA server can return a complete response without Connection: close and then silently close the reusable HTTP/1 transport. The next public bounded-buffer GET or HEAD fails at response-head parsing even though the synchronous adapter already performs guarded one-shot stale recovery. This prevents object-storage reconciliation after a conclusive conditional response.
Solution: Extend guarded stale recovery to complete-body buffer exchanges
Allow public buffer exchanges to use the existing safe-method, no-source, no-response-byte, reused-transport retry path. Add a deterministic native/lightweight established-child regression that models the complete 412 followed by a dropped GET and replacement connection, and record the executable scenario in the maintained corpus.
1cded286The HTTP test action invokes IRI and QUIC as nested Alire projects. After the development index selected a newer compiler, exported GNAT and GPRbuild prefixes from the outer solve contaminated the nested solve, and the QUIC lane maintained a separate incomplete unset list.
Solution: Centralize nested toolchain environment isolation
Clear Flyology, compiler, GPRbuild, and project-path prefixes in the shared nested-test helper, route both IRI and QUIC through it, and extend the action-context regression to cover the toolchain variables.
537edbc5Recover scoped safe reads from silently stale H1 leases
537edbc5 → updated to b5cd966dindex e812215c3 source commits
22d8ee67Scoped describes an operation's limited lifetime and ownership discipline, not the provider that performs it. Keeping client and server operation forms in child namespaces makes callers learn two API trees for one state machine, obscures synchronous parity, and encourages downstream wrappers and documentation drift.
Solution: Colocate composable overloads with their providers
Move the limited constructors, same-name established initiators, operation state accessors, and typed Finish overloads directly into Flyology.HTTP.Client and Flyology.HTTP.Server. Remove both Scoped namespaces without aliases, migrate every executable caller, and retain the existing ownership, certainty, cancellation, drain, deadline, protocol, and guarded stale-H1 behavior.
17a403e5The corpus vocabulary, differential tools, generated expectations, architecture notes, and website guides still name Scoped as a parallel API style. That language would teach the removed namespace and leave provider ownership hard to discover after the intentional development-line source break.
Solution: Document provider-centric composable operations
Rename corpus execution styles to composable buffer and sink forms, regenerate the Ada oracle, and update conformance and differential tooling. Revise the guides and architecture to place root constructors, established initiation, and typed Finish beside synchronous provider calls while preserving scoped lifetime as an operation semantic.
b5cd966dColocate composable HTTP operations with providers
0.1.0-devMemory-node topology reporting for Ada
243833e6 → updated to 810f1088index 36533e238 source commits
86f831ccA slot whose nonwrapping generation reaches its maximum is released back into the free stack. Its next acquisition fails after the pool has already removed it, corrupting availability and potentially leaving waiters with inconsistent accounting. The remoting buffer-domain work depends on generation-safe capability reuse.
Solution: Retire slots after their final published generation
Preflight generation exhaustion before mutating allocation state, omit final-generation slots from the free stack on release, document permanent retirement, and add a statically elided test seam that exercises near-wrap behavior. The regression covers surviving capacity, complete retirement, nonblocking and timed acquisition, coverage integration, and production hook-symbol elision.
06bbc9c0Remoting sessions need to choose among heterogeneous buffer pools while moving payload ownership through definite transport capabilities. A pool-bound buffer alone either exposes an access value across the driver boundary or loses the lifetime, domain, and exception-atomicity guarantees needed for zero-copy delivery.
Solution: Add domain-bound heterogeneous buffer capabilities
Introduce an immutable buffer-domain catalogue with opaque pool references and access-free driver capabilities. Ownership transfers use abort-deferred protected commits, domain-bound public owners retain compile-time lifetime checks, and all storage access validates the supplied domain. Add wrong-domain, zero-copy, finalization, rollback, hook-elision, and compile-negative lifetime coverage.
50b84004A remoting session needs exclusive ownership of one heterogeneous buffer pool while messages are in flight, but the existing domain admitted only ordinary acquisitions. It had no bounded reservation claims, generation-safe delayed release, or authority token for acknowledging a retired session after its final context-state cut. Base buffer acquisition and release also published ownership through protected-call copy-out boundaries that could lose recovery authority under abort.
Solution: Add bounded generation-stamped buffer pool reservations
Add controlled reservation claims and authorized release tokens, exact reservation provenance on buffers and capabilities, total acquisition statuses, nonwrapping generations, pending-release reissue, and permanent exhaustion. Publish and clear actual ownership tokens inside protected actions, retain abort-safe cleanup guards, document lock and lifecycle rules, and cover failure boundaries, provenance, finalization, status precedence, lifetime rejection, and production hook elision.
a4ab6d4bA remoting context must publish a reserved header and an ordinary payload as one abort-deferred ownership transition. The validated buffer operations re-enter the domain gate, so calling them while the context registry is protected would invert the lock boundary and leave no safe compound commit point.
Solution: Add prevalidated direct capability commit moves
Add provider-only limited ownership moves whose bodies contain only same-subtype field transfers and source clearing. Document the caller-owned validation and publication cut, cover heterogeneous pool provenance, tags, bytes, rejection, and abort behavior, and make the root suite fail closed on any unreviewed helper instruction or retained test hook.
f387f8ecThe disabled test-hook variant necessarily contributes its package elaboration and literal Enabled symbols to the production archive. The behavioral suite matches the package prefix broadly, so a clean production build fails before the repository can verify the new buffer-domain boundary.
Solution: Match only executable buffer test seams
Restrict the archive classifier to the arm, consume, note, and live hook operations plus the imported disabled sentinels. This keeps surviving test behavior detectable without treating the inert disabled package metadata as production test code.
c9125bedThe new domain catalogue, sentinels, acquisition results, reservation overloads, and private controlled-operation helpers lack complete GNATdoc annotations. Strict API generation therefore rejects the reviewed dependency series and cannot publish its public contract.
Solution: Complete buffer domain API annotations
Document every new public entity and result literal, describe each reservation overload formal, and exclude private implementation helpers while documenting their formals as required by GNATdoc. Format the specification with the owning Flyology project settings.
5c3d43c4The generated API describes Reservation_Not_Active as never published even though a previously published exact generation can later become inactive. It also attributes the bounded claim capacity to a reservation although ordinary unreserved acquisitions share the same pool claim bound.
Solution: Describe status outcomes by current pool state
Define Reservation_Not_Active in terms of the exact generation currently being inactive and attribute Claim_Limit_Reached to the pool claim capacity. The wording now matches the implemented and tested status paths.
810f1088Add abort-safe heterogeneous buffer domains for remoting
810f1088 → updated to 3859f412index ba75376c2 source commits
84b425b1The out-of-tree consumer does not reference the buffer-domain packages. A stale exact-version Alire lock can therefore supply a pre-domain Flyology source tree while the consumer still builds, leaving the published dependency surface unverified.
Solution: Compile the buffer-domain API out of tree
Make the external consumer import Domains and Domains.Drivers, acquire and release a domain-owned buffer, and call the driver diagnostic so the two package specifications and bodies must be packaged, compiled, and linked.
3859f412Verify buffer-domain API from an external consumer
0.1.3-devAda-native QUIC transport for Flyology tasks
a65f24f4 → updated to 537edbc5index 519c45e43 source commits
f1e65c3dA server can return a complete response without Connection: close and then silently close the reusable HTTP/1 transport. The next public bounded-buffer GET or HEAD fails at response-head parsing even though the synchronous adapter already performs guarded one-shot stale recovery. This prevents object-storage reconciliation after a conclusive conditional response.
Solution: Extend guarded stale recovery to complete-body buffer exchanges
Allow public buffer exchanges to use the existing safe-method, no-source, no-response-byte, reused-transport retry path. Add a deterministic native/lightweight established-child regression that models the complete 412 followed by a dropped GET and replacement connection, and record the executable scenario in the maintained corpus.
1cded286The HTTP test action invokes IRI and QUIC as nested Alire projects. After the development index selected a newer compiler, exported GNAT and GPRbuild prefixes from the outer solve contaminated the nested solve, and the QUIC lane maintained a separate incomplete unset list.
Solution: Centralize nested toolchain environment isolation
Clear Flyology, compiler, GPRbuild, and project-path prefixes in the shared nested-test helper, route both IRI and QUIC through it, and extend the action-context regression to cover the toolchain variables.
537edbc5Recover scoped safe reads from silently stale H1 leases
537edbc5 → updated to b5cd966dindex e812215c3 source commits
22d8ee67Scoped describes an operation's limited lifetime and ownership discipline, not the provider that performs it. Keeping client and server operation forms in child namespaces makes callers learn two API trees for one state machine, obscures synchronous parity, and encourages downstream wrappers and documentation drift.
Solution: Colocate composable overloads with their providers
Move the limited constructors, same-name established initiators, operation state accessors, and typed Finish overloads directly into Flyology.HTTP.Client and Flyology.HTTP.Server. Remove both Scoped namespaces without aliases, migrate every executable caller, and retain the existing ownership, certainty, cancellation, drain, deadline, protocol, and guarded stale-H1 behavior.
17a403e5The corpus vocabulary, differential tools, generated expectations, architecture notes, and website guides still name Scoped as a parallel API style. That language would teach the removed namespace and leave provider ownership hard to discover after the intentional development-line source break.
Solution: Document provider-centric composable operations
Rename corpus execution styles to composable buffer and sink forms, regenerate the Ada oracle, and update conformance and differential tooling. Revise the guides and architecture to place root constructors, established initiation, and typed Finish beside synchronous provider calls while preserving scoped lifetime as an operation semantic.
b5cd966dColocate composable HTTP operations with providers
61a792c6 on GitHub
0.1.2-devPortable, strongly typed SIMD foundations for Ada
5db3e48d on GitHub
0.1.1-devPortable, strongly typed SIMD foundations for Ada
49978181 on GitHub
0.1.1-devA typed, declarative terminal user-interface toolkit for Ada
0.1.1-devHTTP client and server library for Flyology tasks
dac7fa90 → updated to 92820c5findex 9afc79c346 source commits
7a4ec6c3Request limits, decoded-byte counters, route policy, metrics, and diagnostics use Natural throughout the server stack. Values above the platform Natural range can therefore fail during framing or truncate accounting, while the global 1 MiB protocol ceiling prevents S3-scale streaming even when applications retain nothing.
Solution: Share one 64-bit-safe body size across client and server
Introduce Flyology.HTTP.Body_Size and carry it through HTTP/1, HTTP/2, HTTP/3, routing, exchanges, logs, and metrics. Raise the streaming protocol ceiling to the current 50 TB S3 object bound while preserving independent ingress limits and the HTTP/3 profile's existing 1 MiB retained-body cap. Make decimal, hexadecimal, accumulation, and metric arithmetic overflow-safe; add 5 GiB and out-of-range framing regressions; and document the streaming-versus-retention boundary.
ca058efa* Problem: S3 request fidelity and streaming stop at protocol boundaries
Solution: preserve authority, physical headers, and request trailers through every server engine; stream borrowed uploads through bounded HTTP/2 and HTTP/3 flow-control paths; raise bounded target metadata limits; and qualify canonicalization, trailers, and cross-boundary uploads.
* Problem: streaming backpressure can starve protocol progress
Solution: make H2 wait predicates fragment-size aware, poll output immediately before sleeping, stop H3 uploads on early final responses, and isolate H3 qualification runtimes. Cover zero progress, overrun, source exceptions, cancellation, deadlines, early finals, reuse, and generated 64 GiB sources in both protocol integrations.
ffe9cb00Solution: release flyology_quic 0.1.1 with a stable Flyology dependency range, validated against the published 0.1.0 implementation.
5638f1c6Problem: HTTP S3 prerequisites have no stable release (#27)80609dd7Flyology HTTP and Flyology QUIC 0.1.1 have published tags, but users cannot see the notable changes since 0.1.0 or trace them to their pull requests and commits.
Solution: Add crate-scoped release changelogs
Document both release ranges in Keep a Changelog format with version comparisons and source links.
8279a99dObject storage handlers know representation sizes but the server only offers retained responses or unknown-length streams. That forces HTTP/1.1 chunking, prevents correct HEAD metadata, and leaves overruns and underruns unchecked.
Solution: Add proved fixed-length streaming responses
Expose 64-bit Content_Length overloads through the raw and application APIs, enforce exact completion across HTTP/1.1, HTTP/2, and HTTP/3, preserve HEAD suppression and unknown-length compatibility, and qualify framing, failures, isolation, backpressure, large logical lengths, proofs, and documentation.
548a41c8Add the new server API, exact framing and completion guarantees, HEAD behavior, and stream-isolation semantics to the unreleased HTTP changelog.
8cc0e4c5Expose 64-bit known-length streams across HTTP/1.1, HTTP/2, and HTTP/3 with proved exact accounting, correct HEAD semantics, bounded backpressure, failure isolation, tests, and documentation.
80e7f6b9fix(http2): accept empty HEAD stream completionfc54793dfix(http2): accept empty HEAD stream completion
4e93366frelease flyology_http 0.1.28f34e73brelease flyology_http 0.1.2
2e356498Request-head input inspection and chunked-body framing combine transport suspension with parser progress. In particular, trailer phase and byte accounting are local to a blocking loop, so a scoped operation cannot suspend and resume without duplicating parser semantics or retaining a helper stack.
Solution: Factor HTTP input parsing into bounded immediate steps
Separate request initialization and buffered-input inspection, promote trailer progress into the connection state, and advance body framing one bounded transition at a time. Keep the synchronous entry points driving the same steps, and exercise every header, chunk, terminator, and trailer boundary with one-byte transport fragmentation.
a1b43b16Changing the request wait path can alter the existing native and lightweight HTTP performance profile. Without an untouched c128 cohort and an explicit foundation pin, later throughput or latency changes cannot be attributed or reproduced.
Solution: Pin the Flyology operation branch and record baseline evidence
Track codex/composable-operations through a branch pin that remains easy to advance, and document the exact baseline commits, environment, 16-loop runtime, commands, three-sample throughput and latency distributions, resource controls, noise caveats, and pre-existing nested-suite mismatch.
8e1b04a5The public request-head and streaming-body entry points suspend directly through a class-wide transport. Callers cannot combine them with Flyology timers, gates, cancellation, connection setup, or counted completion waits while preserving connection and borrowed-buffer lifetimes.
Solution: add bounded scoped HTTP server operations
Add a public immediate-step transport capability backed by the set-independent connection driver, scoped request-head, body-acceptance, and body-read operations, and retained typed Finish procedures. Share bounded parser transitions with the synchronous API, initialize closure state on every parse path, preserve original failures through cleanup, and cover native and lightweight composition, TLS upgrade, cancellation, failure retention, cleanup, partial I/O, and synchronous parity.
d88e70cbThe controlled c128 baseline has no post-change measurements, so reviewers cannot distinguish steady-state regression from host noise or verify the final success count and latency profile.
Solution: record the current-pin candidate campaign
Document the exact Flyology and HTTP revisions, qualification commands, three samples per workload and lane, median throughput and latency, baseline deltas, all-success response count, and the observed run-order noise. Mark the results as current-pin evidence that must be repeated when the foundation revision advances.
fcac4635The final composable-operations foundation refactors the private TLS descriptor controller around explicit operation states. The HTTP repository's test-only child body still calls the removed acquisition primitives, so its authoritative suite cannot compile against the advanced branch pin.
Solution: synchronize the TLS test adapter state transitions
Use Start_Operation, Try_Acquire, Abandon_Operation, and state-aware Release exactly as the foundation test helper does. Preserve the existing Generation and stale-acquisition test behavior without adding any production dependency on the private controller.
18a9ad70The intermediate benchmark covers an earlier Flyology operation-provider revision and ran while unrelated stale compiler probes loaded the host. It cannot qualify the replacement PR #60 head or distinguish a synchronous-path regression from host contention.
Solution: record the serialized final c128 campaign
Document fresh suite and GNATdoc validation on exact Flyology 195b228, all three alternating 16-loop and native trials, latency and throughput summaries, HTTP status results, and wrapper resource evidence. State the contention caveat and the supported conclusion that the final candidate preserves the established performance profile.
8de52f57Flyology composable operations are merged and published as 0.1.1-dev in the organization index. Keeping the mutable Git branch override bypasses that validated release resolution and leaves the consumer tied to review infrastructure.
Solution: resolve Flyology from the organization index
Require flyology 0.1.1-dev and remove the custom Git pin. The resolved release is the merged main commit 159210ca44eff9f636e933195bf176c3500ff60e.
a91e1a77The manifest test action exports ALIRE and Flyology paths without the private HTTP test marker. The test entry can therefore invoke a nested root build, and its IRI child inherits outer dependency paths into a separate Alire workspace.
Solution: reuse the active Alire test context
Recognize the standard action environment before any build decision, export the existing marker, and sanitize outer Flyology paths only for the IRI child workspace. Add a shell regression for action-marker inheritance, child isolation, and unchanged direct invocation.
f4d98b2cThe low-level HTTP/1.x server API exposes bounded request-head and body operations, but the website does not explain their transport requirements, lifecycle, composition rules, or ownership limits.
Solution: Add a bounded operations guide
Document synchronous versus scoped calls, operation-capable plain and upgraded-TLS transports, slot sizing, typed Finish, body sequencing, waits, gates, cancellation, retained failures, and borrowed lifetimes. Link the guide from the guide index, server guide, and llms.txt.
f4fd9c3eAdd composable HTTP server operations
6f72c29bThe application stream reassembler is fixed at the initial receive window and the connection cannot return MAX_STREAM_DATA credit. Whole HTTP/3 responses above 16 KiB therefore stall, and old retransmissions below the consumed base can underflow reassembly offsets.
Solution: add rolling stream receive credit
Track consumed stream bases, encode and schedule MAX_STREAM_DATA, preserve final-size and overlap checks across window advances, and cover credit, reuse, overlap, and retired retransmission behavior with policy tests.
f0d630d0Object storage and database providers cannot compose admission, upload, response-head, and complete-body work on their completion-set owner stack. The synchronous client uses a separate monolithic path, which duplicates H1/H2/H3 policy and can replay ambiguous conditional mutations.
Solution: make the composable exchange engine canonical
Add limited root and established exchange operations for owned buffers and streaming sinks, transactional Unique_Buffer restoration, monotonic admission certainty, typed environmental outcomes, one absolute deadline, owner-driven DNS/connect/TLS/H1/H2/H3 children, multiplexed stream isolation, and cancellation drain. Route synchronous Execute through the same request and response-head engine while preserving its retained-response API and exception semantics.
3d72adbcThe synchronous and composable APIs need the same deterministic scenarios across H1, H2, and H3, including ambiguity, ownership restoration, blocked sources, cancellation drain, malformed multiplexed responses, and large flow-controlled bodies. Existing protocol tests do not provide one shared golden oracle or complete CI coverage.
Solution: gate both APIs with a shared client corpus
Add standards-backed JSON scenarios, checked-in binary and hex fixtures, generated Ada expectations, source and sink fault providers, native/lightweight scoped matrices, h2specd client coverage, aioquic and PycURL differentials, large-body reuse, lost-final-response and no-replay oracles, and required CI execution. Ignore Python bytecode artifacts.
4b1b91c3Users cannot discover the scoped complete-body operations, ownership restoration, admission certainty, parent composition, readiness bounds, or the fact that synchronous Execute now uses the same canonical protocol engine.
Solution: document composable client exchanges
Add a complete buffer and sink workflow, child-operation example, cancellation and ambiguity rules, protocol readiness bounds, task-free multiplexing details, current address iteration behavior, corpus evidence boundaries, and development-version changelog entries.
d60726a8Fresh clones need a deterministic way to provision Codex and Claude resources without committed skill symlinks. Repository-specific instruction scopes also need to remain owned and versioned with the code they govern instead of becoming shared policy accidentally.
Solution: Compose local and shared APM packages
Package each existing repository scope under agent-packages, pin the validated shared profile at an exact commit, and commit the resolved lockfile and generated Codex guides. Document pinned APM installation and add CI that installs Claude rules and both skill trees, compiles Codex instructions, audits the graph, and checks drift.
4e5fc625Multi-stream packet validation copies the complete bounded stream table before reassembly. The copy adds roughly a megabyte to the owner task's deepest HTTP/3 receive path, so sustained exchanges can overflow an otherwise bounded Flyology task stack.
Solution: commit stream reassembly in place after packet validation
Apply validated stream frames directly and rely on the connection-fatal reassembly-conflict boundary to prevent later reuse. Add a driver-level poison-and-no-reuse regression and explicit SPARK witnesses for the receive-window and stream-table invariants; the complete QUIC policy suite proves all 2,156 checks.
37b7cbbbSynchronous compatibility calls accumulate limited function-return temporaries in long caller scopes, while HTTP/3 keeps large event scratch in exchange and body-reader frames. Retry recursion and protocol cleanup diagnostics add further depth, and the HTTP/2 bounded-body check misclassifies representation lengths on body-forbidden responses.
Solution: reuse results and persistent protocol scratch
Add procedural Execute and Read_All overloads that drive the same composable engine, make redirect retries iterative, and retain one serialized HTTP/3 event per pooled connection. Remove the legacy synchronous HTTP/3 body path, preserve causal drain phases, flush queued HTTP/2 shutdown work, and align body-forbidden capacity semantics across protocols. Gate the result with 10,000-exchange HTTP/1.1 and HTTP/2 loops, reduced-stack concurrent 8,000-exchange HTTP/3 clients, scoped HEAD/304 buffer regressions, and the repaired deterministic conformance harness.
2f8add5dThe shared package is pinned to a full commit in apm.yml, duplicating the
lockfile and preventing apm update from advancing the reviewed dependency
without rewriting the manifest.
Solution: Track agents main through the lockfile
Declare main as the update channel, refresh the exact lockfile pin, and
document the explicit update, compilation, and audit workflow.
aba55512The extended H1 and H2 reuse regressions are intended to detect stack growth and slot leaks across 10,000 or more exchanges. On a slow runner, the default total connection age expires during the H2 loop. The client correctly retires that transport, but the single-connection independent peer exits and the test misreports the expected rotation as a client failure. Its truncated peer log also hides the terminal request count.
Solution: Isolate reuse stress from pool age policy
Disable total-age rotation only in the long H1 and H2 reuse scenarios, consistent with the existing HTTP/2 soak qualification. Keep rotation under the separate deterministic pool-policy tests. Report both ends and aggregate counts from a failed HTTP/2 peer trace so future long-run failures retain useful evidence.
7daf0b4aThe locked shared package graph predates the findings-driven review rule, so
Codex and Claude in this repository do not yet require architectural decisions
and implementation changes to close P0 and P1 findings or explicitly dispose
of P2 findings before completion.
Solution: Adopt the shared review-cycle package
Advance the exact agents lock to the reviewed shared commit and regenerate
Codex instructions so both clients receive the same remediation and repeat-
review requirements.
ef6c8d9cThe locked shared skill reports hard-coded value decisions but does not require
their durable authority to be recorded beside Ada declarations. Future changes
can therefore mistake protocol mandates, derived arithmetic, or test capacities
for arbitrary production policy.
Solution: Adopt source-adjacent authority documentation
Advance the exact shared agents lock to the reviewed constants skill. The
updated portable workflow preserves consequential choices and their stable
authority in comments while excluding harmless literals from comment noise.
8a527902The composable and synchronous HTTP/2 client rejects a malformed frame from its header before consuming a bounded declared payload. On Linux, closing the socket with those peer bytes unread can replace the queued GOAWAY with a TCP reset, so h2spec cannot observe the required protocol error. Invalid frame lengths also use the generic protocol error instead of FRAME_SIZE_ERROR.
Solution: Drain bounded malformed frames before protocol failure
Wait for and consume the declared payload when the complete malformed frame fits the fixed input buffer, matching the server-side defensive path. Select FRAME_SIZE_ERROR for excessive or invalid frame lengths and retain PROTOCOL_ERROR for other invalid headers.
6e03ebceRepositories using the shared agents package remain pinned before the crate-spinout workflow and the refined constants-audit triage. Fresh sessions therefore cannot discover those reviewed capabilities.
Solution: Refresh the shared agents lock
Advance every shared package entry to the green agents main revision. The manifest keeps its main update channel while the lock preserves the exact commit and content hashes for reproducible Codex and Claude installs.
882e2440After a peer protocol violation, the owner-stack pump writes GOAWAY and immediately releases the connection lease. Linux can turn that close into a TCP reset while peer input is still arriving, so h2spec cannot observe the required error on semantic CONTINUATION violations. Consuming malformed frame payloads alone does not cover these validly framed protocol errors.
Solution: Drain protocol-error closes compositionally
Retain the pump and transport lease after GOAWAY, discard one bounded receive chunk per owner-stack drive, and finish on peer close or a one-second monotonic cleanup deadline capped by the request deadline. Readiness and the cleanup timer remain on the existing operation slot, and normal exchanges are unchanged.
41471605The 8,000-exchange HTTP/3 regression is intended to keep one connection alive while crossing stream-table and flow-control credit boundaries. Its test server still has a 20-second total connection age, so a loaded host retires the peer around exchange 5,000 to 7,000 and reports a timeout unrelated to stack or credit reuse.
Solution: Isolate HTTP/3 reuse stress from age policy
Disable total-age rotation only for the long-lived HTTP/3 fixture, matching the existing H1 and H2 reuse stress setup. Deterministic pool-policy tests continue to cover connection-age rotation.
5f23f559A client can append a forbidden TLS message such as KeyUpdate after Finished in one Handshake CRYPTO range. The server accepts Finished and publishes Connected before checking the trailing message, so rejection depends on a later Handshake packet and h3spec can observe a successful connection.
Solution: Reject complete trailing Handshake messages before connection publication
Inspect the already-reassembled CRYPTO bytes immediately after Finished validation and emit the unexpected_message crypto close before HANDSHAKE_DONE. Repeat the exact published h3spec boundary ten times in the qualification gate to keep the scheduling-independent behavior executable.
db0346bbThe timeout and cancellation fixture expects abandoned HTTP/1 connections to end only with a zero-length receive. macOS can report a typed socket reset when the client closes with unread bytes, causing the raw server to abort and the following exchange to time out even though the client behavior is correct.
Solution: Accept reset as terminal in the close-only fixture path
Treat Socket_Error as peer termination only inside Expect_Close while preserving hard failures for request-head reads and response writes. The corrected fixture completes both native and lightweight lanes across 100 consecutive runs.
262b0c2fThe qualification runner prints only the first peer events when a concurrent soak fails. A late lost-wakeup or stream-lifecycle stall therefore reports an old request rather than the active streams and event counts needed to distinguish protocol and scheduling failures.
Solution: Preserve terminal HTTP/2 soak diagnostics
Record completed request halves and timeout-time active streams in the independent peer. Print bounded first and final event windows plus aggregate request, response, reset, and timeout counts on failure.
98c0e26fA just-rescheduled stream can repeatedly reclaim the shared session pump while sibling streams only receive broadcast wakeups. Under concurrent load a sibling can therefore wait through its absolute request deadline even though the transport keeps making progress.
Solution: Hand off the HTTP/2 pump fairly
Track bounded waiter state inside the protected session controller and reserve each released pump for the next waiter in round-robin order. Cover the no-cut-in handoff sequence with a deterministic four-stream regression.
23051ca7The synchronous response-body adapter returns as soon as its stream is locally ready or already complete. If an invalid peer frame is received with the final response, that path can release the transport after queuing GOAWAY but before the bounded close drain, replacing the protocol error with TCP reset.
Solution: Preserve composable drain semantics in the sync adapter
Keep driving the shared pump through its reported closing-drain budget regardless of local stream readiness, and settle already-complete H2 bodies before returning. The adapter uses the same pump, monotonic request deadline, and bounded one-second protocol-close budget as the composable exchange.
b7fb5474The conformance executable exits after one full exchange. A malicious frame sent just after the response reaches would-block can arrive after the last operation terminalizes, so the peer observes a TCP reset instead of the required HTTP/2 error response. This made closed-stream h2spec cases timing-dependent in CI.
Solution: give conformance exchanges a bounded settlement probe
Add a per-client testing-only grace interval that defaults to zero. The synchronous and scoped adapters use their existing owner-stack pump, transport readiness, completion slot, and absolute deadline for one bounded post-response probe. Enable 50 ms only in the h2specd driver and document the qualification boundary.
153c9c80The 50 ms conformance-only readiness interval can expire before a lightweight-owner peer schedules a CONTINUATION following RST_STREAM. The client then exits and h2spec observes a TCP reset instead of the queued HTTP/2 stream or connection error.
Solution: widen the bounded conformance probe to 250 ms
Keep the production default at zero while giving the single-exchange h2specd driver enough owner-stack readiness time under CI load. Document the exact bound; no helper task, protocol path, completion slot, or application latency changes.
09e95348The composable-client branch declares flyology_http 0.1.3-dev and flyology_quic 0.1.2-dev even though this work is published by advancing the existing 0.1.1-dev sessions without a stable release. The mismatch would force unnecessary index-version bootstrapping and give consumers the wrong session coordinates.
Solution: keep both active sessions at 0.1.1-dev
Restore the HTTP and QUIC crate versions and the exact HTTP-to-QUIC development constraint to 0.1.1-dev. Record that this work advances development origins only and creates no stable release tag.
92820c5fAdd composable owner-driven HTTP client exchanges
92820c5f → updated to 92820c5findex 5998ae17flyology_quic ~0.1.1-dev→ changed to =0.1.1-dev0.1.1-devAllocation-conscious URI, IRI, and URL reference parser
8f34e73b → updated to 92820c5findex 3903559734 source commits
2e356498Request-head input inspection and chunked-body framing combine transport suspension with parser progress. In particular, trailer phase and byte accounting are local to a blocking loop, so a scoped operation cannot suspend and resume without duplicating parser semantics or retaining a helper stack.
Solution: Factor HTTP input parsing into bounded immediate steps
Separate request initialization and buffered-input inspection, promote trailer progress into the connection state, and advance body framing one bounded transition at a time. Keep the synchronous entry points driving the same steps, and exercise every header, chunk, terminator, and trailer boundary with one-byte transport fragmentation.
a1b43b16Changing the request wait path can alter the existing native and lightweight HTTP performance profile. Without an untouched c128 cohort and an explicit foundation pin, later throughput or latency changes cannot be attributed or reproduced.
Solution: Pin the Flyology operation branch and record baseline evidence
Track codex/composable-operations through a branch pin that remains easy to advance, and document the exact baseline commits, environment, 16-loop runtime, commands, three-sample throughput and latency distributions, resource controls, noise caveats, and pre-existing nested-suite mismatch.
8e1b04a5The public request-head and streaming-body entry points suspend directly through a class-wide transport. Callers cannot combine them with Flyology timers, gates, cancellation, connection setup, or counted completion waits while preserving connection and borrowed-buffer lifetimes.
Solution: add bounded scoped HTTP server operations
Add a public immediate-step transport capability backed by the set-independent connection driver, scoped request-head, body-acceptance, and body-read operations, and retained typed Finish procedures. Share bounded parser transitions with the synchronous API, initialize closure state on every parse path, preserve original failures through cleanup, and cover native and lightweight composition, TLS upgrade, cancellation, failure retention, cleanup, partial I/O, and synchronous parity.
d88e70cbThe controlled c128 baseline has no post-change measurements, so reviewers cannot distinguish steady-state regression from host noise or verify the final success count and latency profile.
Solution: record the current-pin candidate campaign
Document the exact Flyology and HTTP revisions, qualification commands, three samples per workload and lane, median throughput and latency, baseline deltas, all-success response count, and the observed run-order noise. Mark the results as current-pin evidence that must be repeated when the foundation revision advances.
fcac4635The final composable-operations foundation refactors the private TLS descriptor controller around explicit operation states. The HTTP repository's test-only child body still calls the removed acquisition primitives, so its authoritative suite cannot compile against the advanced branch pin.
Solution: synchronize the TLS test adapter state transitions
Use Start_Operation, Try_Acquire, Abandon_Operation, and state-aware Release exactly as the foundation test helper does. Preserve the existing Generation and stale-acquisition test behavior without adding any production dependency on the private controller.
18a9ad70The intermediate benchmark covers an earlier Flyology operation-provider revision and ran while unrelated stale compiler probes loaded the host. It cannot qualify the replacement PR #60 head or distinguish a synchronous-path regression from host contention.
Solution: record the serialized final c128 campaign
Document fresh suite and GNATdoc validation on exact Flyology 195b228, all three alternating 16-loop and native trials, latency and throughput summaries, HTTP status results, and wrapper resource evidence. State the contention caveat and the supported conclusion that the final candidate preserves the established performance profile.
8de52f57Flyology composable operations are merged and published as 0.1.1-dev in the organization index. Keeping the mutable Git branch override bypasses that validated release resolution and leaves the consumer tied to review infrastructure.
Solution: resolve Flyology from the organization index
Require flyology 0.1.1-dev and remove the custom Git pin. The resolved release is the merged main commit 159210ca44eff9f636e933195bf176c3500ff60e.
a91e1a77The manifest test action exports ALIRE and Flyology paths without the private HTTP test marker. The test entry can therefore invoke a nested root build, and its IRI child inherits outer dependency paths into a separate Alire workspace.
Solution: reuse the active Alire test context
Recognize the standard action environment before any build decision, export the existing marker, and sanitize outer Flyology paths only for the IRI child workspace. Add a shell regression for action-marker inheritance, child isolation, and unchanged direct invocation.
f4d98b2cThe low-level HTTP/1.x server API exposes bounded request-head and body operations, but the website does not explain their transport requirements, lifecycle, composition rules, or ownership limits.
Solution: Add a bounded operations guide
Document synchronous versus scoped calls, operation-capable plain and upgraded-TLS transports, slot sizing, typed Finish, body sequencing, waits, gates, cancellation, retained failures, and borrowed lifetimes. Link the guide from the guide index, server guide, and llms.txt.
f4fd9c3eAdd composable HTTP server operations
6f72c29bThe application stream reassembler is fixed at the initial receive window and the connection cannot return MAX_STREAM_DATA credit. Whole HTTP/3 responses above 16 KiB therefore stall, and old retransmissions below the consumed base can underflow reassembly offsets.
Solution: add rolling stream receive credit
Track consumed stream bases, encode and schedule MAX_STREAM_DATA, preserve final-size and overlap checks across window advances, and cover credit, reuse, overlap, and retired retransmission behavior with policy tests.
f0d630d0Object storage and database providers cannot compose admission, upload, response-head, and complete-body work on their completion-set owner stack. The synchronous client uses a separate monolithic path, which duplicates H1/H2/H3 policy and can replay ambiguous conditional mutations.
Solution: make the composable exchange engine canonical
Add limited root and established exchange operations for owned buffers and streaming sinks, transactional Unique_Buffer restoration, monotonic admission certainty, typed environmental outcomes, one absolute deadline, owner-driven DNS/connect/TLS/H1/H2/H3 children, multiplexed stream isolation, and cancellation drain. Route synchronous Execute through the same request and response-head engine while preserving its retained-response API and exception semantics.
3d72adbcThe synchronous and composable APIs need the same deterministic scenarios across H1, H2, and H3, including ambiguity, ownership restoration, blocked sources, cancellation drain, malformed multiplexed responses, and large flow-controlled bodies. Existing protocol tests do not provide one shared golden oracle or complete CI coverage.
Solution: gate both APIs with a shared client corpus
Add standards-backed JSON scenarios, checked-in binary and hex fixtures, generated Ada expectations, source and sink fault providers, native/lightweight scoped matrices, h2specd client coverage, aioquic and PycURL differentials, large-body reuse, lost-final-response and no-replay oracles, and required CI execution. Ignore Python bytecode artifacts.
4b1b91c3Users cannot discover the scoped complete-body operations, ownership restoration, admission certainty, parent composition, readiness bounds, or the fact that synchronous Execute now uses the same canonical protocol engine.
Solution: document composable client exchanges
Add a complete buffer and sink workflow, child-operation example, cancellation and ambiguity rules, protocol readiness bounds, task-free multiplexing details, current address iteration behavior, corpus evidence boundaries, and development-version changelog entries.
d60726a8Fresh clones need a deterministic way to provision Codex and Claude resources without committed skill symlinks. Repository-specific instruction scopes also need to remain owned and versioned with the code they govern instead of becoming shared policy accidentally.
Solution: Compose local and shared APM packages
Package each existing repository scope under agent-packages, pin the validated shared profile at an exact commit, and commit the resolved lockfile and generated Codex guides. Document pinned APM installation and add CI that installs Claude rules and both skill trees, compiles Codex instructions, audits the graph, and checks drift.
4e5fc625Multi-stream packet validation copies the complete bounded stream table before reassembly. The copy adds roughly a megabyte to the owner task's deepest HTTP/3 receive path, so sustained exchanges can overflow an otherwise bounded Flyology task stack.
Solution: commit stream reassembly in place after packet validation
Apply validated stream frames directly and rely on the connection-fatal reassembly-conflict boundary to prevent later reuse. Add a driver-level poison-and-no-reuse regression and explicit SPARK witnesses for the receive-window and stream-table invariants; the complete QUIC policy suite proves all 2,156 checks.
37b7cbbbSynchronous compatibility calls accumulate limited function-return temporaries in long caller scopes, while HTTP/3 keeps large event scratch in exchange and body-reader frames. Retry recursion and protocol cleanup diagnostics add further depth, and the HTTP/2 bounded-body check misclassifies representation lengths on body-forbidden responses.
Solution: reuse results and persistent protocol scratch
Add procedural Execute and Read_All overloads that drive the same composable engine, make redirect retries iterative, and retain one serialized HTTP/3 event per pooled connection. Remove the legacy synchronous HTTP/3 body path, preserve causal drain phases, flush queued HTTP/2 shutdown work, and align body-forbidden capacity semantics across protocols. Gate the result with 10,000-exchange HTTP/1.1 and HTTP/2 loops, reduced-stack concurrent 8,000-exchange HTTP/3 clients, scoped HEAD/304 buffer regressions, and the repaired deterministic conformance harness.
2f8add5dThe shared package is pinned to a full commit in apm.yml, duplicating the
lockfile and preventing apm update from advancing the reviewed dependency
without rewriting the manifest.
Solution: Track agents main through the lockfile
Declare main as the update channel, refresh the exact lockfile pin, and
document the explicit update, compilation, and audit workflow.
aba55512The extended H1 and H2 reuse regressions are intended to detect stack growth and slot leaks across 10,000 or more exchanges. On a slow runner, the default total connection age expires during the H2 loop. The client correctly retires that transport, but the single-connection independent peer exits and the test misreports the expected rotation as a client failure. Its truncated peer log also hides the terminal request count.
Solution: Isolate reuse stress from pool age policy
Disable total-age rotation only in the long H1 and H2 reuse scenarios, consistent with the existing HTTP/2 soak qualification. Keep rotation under the separate deterministic pool-policy tests. Report both ends and aggregate counts from a failed HTTP/2 peer trace so future long-run failures retain useful evidence.
7daf0b4aThe locked shared package graph predates the findings-driven review rule, so
Codex and Claude in this repository do not yet require architectural decisions
and implementation changes to close P0 and P1 findings or explicitly dispose
of P2 findings before completion.
Solution: Adopt the shared review-cycle package
Advance the exact agents lock to the reviewed shared commit and regenerate
Codex instructions so both clients receive the same remediation and repeat-
review requirements.
ef6c8d9cThe locked shared skill reports hard-coded value decisions but does not require
their durable authority to be recorded beside Ada declarations. Future changes
can therefore mistake protocol mandates, derived arithmetic, or test capacities
for arbitrary production policy.
Solution: Adopt source-adjacent authority documentation
Advance the exact shared agents lock to the reviewed constants skill. The
updated portable workflow preserves consequential choices and their stable
authority in comments while excluding harmless literals from comment noise.
8a527902The composable and synchronous HTTP/2 client rejects a malformed frame from its header before consuming a bounded declared payload. On Linux, closing the socket with those peer bytes unread can replace the queued GOAWAY with a TCP reset, so h2spec cannot observe the required protocol error. Invalid frame lengths also use the generic protocol error instead of FRAME_SIZE_ERROR.
Solution: Drain bounded malformed frames before protocol failure
Wait for and consume the declared payload when the complete malformed frame fits the fixed input buffer, matching the server-side defensive path. Select FRAME_SIZE_ERROR for excessive or invalid frame lengths and retain PROTOCOL_ERROR for other invalid headers.
6e03ebceRepositories using the shared agents package remain pinned before the crate-spinout workflow and the refined constants-audit triage. Fresh sessions therefore cannot discover those reviewed capabilities.
Solution: Refresh the shared agents lock
Advance every shared package entry to the green agents main revision. The manifest keeps its main update channel while the lock preserves the exact commit and content hashes for reproducible Codex and Claude installs.
882e2440After a peer protocol violation, the owner-stack pump writes GOAWAY and immediately releases the connection lease. Linux can turn that close into a TCP reset while peer input is still arriving, so h2spec cannot observe the required error on semantic CONTINUATION violations. Consuming malformed frame payloads alone does not cover these validly framed protocol errors.
Solution: Drain protocol-error closes compositionally
Retain the pump and transport lease after GOAWAY, discard one bounded receive chunk per owner-stack drive, and finish on peer close or a one-second monotonic cleanup deadline capped by the request deadline. Readiness and the cleanup timer remain on the existing operation slot, and normal exchanges are unchanged.
41471605The 8,000-exchange HTTP/3 regression is intended to keep one connection alive while crossing stream-table and flow-control credit boundaries. Its test server still has a 20-second total connection age, so a loaded host retires the peer around exchange 5,000 to 7,000 and reports a timeout unrelated to stack or credit reuse.
Solution: Isolate HTTP/3 reuse stress from age policy
Disable total-age rotation only for the long-lived HTTP/3 fixture, matching the existing H1 and H2 reuse stress setup. Deterministic pool-policy tests continue to cover connection-age rotation.
5f23f559A client can append a forbidden TLS message such as KeyUpdate after Finished in one Handshake CRYPTO range. The server accepts Finished and publishes Connected before checking the trailing message, so rejection depends on a later Handshake packet and h3spec can observe a successful connection.
Solution: Reject complete trailing Handshake messages before connection publication
Inspect the already-reassembled CRYPTO bytes immediately after Finished validation and emit the unexpected_message crypto close before HANDSHAKE_DONE. Repeat the exact published h3spec boundary ten times in the qualification gate to keep the scheduling-independent behavior executable.
db0346bbThe timeout and cancellation fixture expects abandoned HTTP/1 connections to end only with a zero-length receive. macOS can report a typed socket reset when the client closes with unread bytes, causing the raw server to abort and the following exchange to time out even though the client behavior is correct.
Solution: Accept reset as terminal in the close-only fixture path
Treat Socket_Error as peer termination only inside Expect_Close while preserving hard failures for request-head reads and response writes. The corrected fixture completes both native and lightweight lanes across 100 consecutive runs.
262b0c2fThe qualification runner prints only the first peer events when a concurrent soak fails. A late lost-wakeup or stream-lifecycle stall therefore reports an old request rather than the active streams and event counts needed to distinguish protocol and scheduling failures.
Solution: Preserve terminal HTTP/2 soak diagnostics
Record completed request halves and timeout-time active streams in the independent peer. Print bounded first and final event windows plus aggregate request, response, reset, and timeout counts on failure.
98c0e26fA just-rescheduled stream can repeatedly reclaim the shared session pump while sibling streams only receive broadcast wakeups. Under concurrent load a sibling can therefore wait through its absolute request deadline even though the transport keeps making progress.
Solution: Hand off the HTTP/2 pump fairly
Track bounded waiter state inside the protected session controller and reserve each released pump for the next waiter in round-robin order. Cover the no-cut-in handoff sequence with a deterministic four-stream regression.
23051ca7The synchronous response-body adapter returns as soon as its stream is locally ready or already complete. If an invalid peer frame is received with the final response, that path can release the transport after queuing GOAWAY but before the bounded close drain, replacing the protocol error with TCP reset.
Solution: Preserve composable drain semantics in the sync adapter
Keep driving the shared pump through its reported closing-drain budget regardless of local stream readiness, and settle already-complete H2 bodies before returning. The adapter uses the same pump, monotonic request deadline, and bounded one-second protocol-close budget as the composable exchange.
b7fb5474The conformance executable exits after one full exchange. A malicious frame sent just after the response reaches would-block can arrive after the last operation terminalizes, so the peer observes a TCP reset instead of the required HTTP/2 error response. This made closed-stream h2spec cases timing-dependent in CI.
Solution: give conformance exchanges a bounded settlement probe
Add a per-client testing-only grace interval that defaults to zero. The synchronous and scoped adapters use their existing owner-stack pump, transport readiness, completion slot, and absolute deadline for one bounded post-response probe. Enable 50 ms only in the h2specd driver and document the qualification boundary.
153c9c80The 50 ms conformance-only readiness interval can expire before a lightweight-owner peer schedules a CONTINUATION following RST_STREAM. The client then exits and h2spec observes a TCP reset instead of the queued HTTP/2 stream or connection error.
Solution: widen the bounded conformance probe to 250 ms
Keep the production default at zero while giving the single-exchange h2specd driver enough owner-stack readiness time under CI load. Document the exact bound; no helper task, protocol path, completion slot, or application latency changes.
09e95348The composable-client branch declares flyology_http 0.1.3-dev and flyology_quic 0.1.2-dev even though this work is published by advancing the existing 0.1.1-dev sessions without a stable release. The mismatch would force unnecessary index-version bootstrapping and give consumers the wrong session coordinates.
Solution: keep both active sessions at 0.1.1-dev
Restore the HTTP and QUIC crate versions and the exact HTTP-to-QUIC development constraint to 0.1.1-dev. Record that this work advances development origins only and creates no stable release tag.
92820c5fAdd composable owner-driven HTTP client exchanges
92820c5f → updated to a65f24f4index 2dcc8dd63 source commits
2444df84HTTP and QUIC still identify their development sessions as 0.1.1-dev. Stable HTTP 0.1.2 and stable QUIC 0.1.1 retire those entries under the Alire index policy, so merged work cannot be published through the development channel and consumers must retain source pins.
Solution: open a refreshable lockstep 0.1.3-dev channel
Advance both indexed crate manifests to 0.1.3-dev, keep the HTTP dependency on QUIC exact at the matching development version, and record that this is a development rollover without stable tags.
686094b1A peer can reset the active stream and immediately send a connection-level violation. The scoped lightweight exchange may finish cancellation and let a short-lived conformance client close between those frames, so h2spec observes TCP RST instead of the required HTTP/2 error handling.
Solution: retain the configured settlement probe through cancellation
When the explicit test-only HTTP/2 settlement grace is nonzero, perform one bounded owner-stack read probe before releasing a cancelled stream. Preserve the zero-duration production default, clear the probe state at terminal cancellation, and document the conformance fix.
a65f24f4Open the HTTP and QUIC 0.1.3-dev channel
b1dea203 on GitHub
0.1.3-devHTTP client and server library for Flyology tasks
flyology =0.1.1-devflyology_cachelines =0.1.0flyology_quic =0.1.3-dev0.1.3-devAda-native QUIC transport for Flyology tasks
flyology ~0.1.057e28b32 on GitHub
0.1.1-devExperimental GNAT runtime extension for native and lightweight Ada tasks
0.1.0-devCaller-owned memory allocation algorithms for Ada
0.1.1-devAdaptive nanosecond-scale benchmarking for Ada
0.1.1-devCache-line-aware storage and cache information for Ada
0.1.1-devBounded in-memory tracing and gauges for Ada
0.1.0-devMemory-node topology reporting for Ada
9afc79c3 on GitHub
0.1.1-devAda-native QUIC transport for Flyology tasks
0.1.1-devExperimental GNAT runtime extension for native and lightweight Ada tasks
ef6841e7 → updated to 4ec71932index 73e6c1b57 source commits
c9ccf71bFlyology lacks a generation protocol, listener-capability handoff, and coordinated canary lifecycle for replacing a running server with a changed executable. Operators cannot prepare, admit, promote, cancel, drain, fence, or reverse generations through one reviewed ownership model.
Solution: Add supervised process-generation upgrades and executable guide
Implement the Ada-owned coordinator and agent protocol, narrow descriptor and bootstrap handoff mechanisms, lifecycle policy, ABI and behavioral checks, and a mechanically extracted two-artifact website guide that exercises promotion and cancellation.
1fb1025eReaders can follow coordinator calls without knowing what behavior changes between the two artifacts or what the verification request proves.
Solution: Explain the example server and its observable cutover
Describe the one-byte request/reply behavior, the distinct generation replies, and how promotion and cancellation assertions relate to new connections.
5ec82af6Add supervised process-generation upgrades
270984a3The standalone flyology_bench Alire action captures command output in alire/alr_test_local.log. Hosted failures currently retain only Alire’s generic wrapper, so maintainers cannot identify which redirected smoke executable or assertion failed.
Solution: Surface benchmark test diagnostics on failure
Print the tail of the captured Alire log while preserving the original status, and label the two redirected smoke executables so the retained log names the active command. Successful test output remains unchanged apart from the command labels.
0081ba78The flyology_bench smoke fixture accumulates every calibrated operation in a Natural. Fast x86-64 runners can execute more than Natural Last operations across the suite, raising Constraint_Error before the runtime-specific tests begin.
Solution: Use a modular 64-bit operation counter
Store the volatile sink and scheduler-probe value directly as Interfaces.Unsigned_64. The counter now retains the intended observable work signal without a checked 32-bit accumulation limit.
04ff6dcd* Problem: Managed clients require raw socket adoption boilerplate
Composable protocol clients need a managed Flyology connection before they can use the public connection driver, but callers must create and connect a raw socket and then transfer it through Take. This obscures the admission, deadline, cancellation, and cleanup boundary.
Solution: Add a direct managed connection path
Add Connections.Connect to reserve admission, create and connect the endpoint-family socket under one deadline, and atomically adopt it with existing guards. Cover both lanes, failure cleanup, TLS and driver composition, and document raw and managed client choices.
* Problem: The managed connection test iterates a platform type
Execution_Model is discrete on Darwin but an access type on Linux, so iterating its subtype prevents the new smoke test from compiling on a supported platform. The Darwin loop also repeats the configured project default rather than naming the two lanes under test.
Solution: Run the two execution lanes explicitly
Move the scenarios into Run_All and invoke it once for Lightweight_Task and once for Native_Task. This preserves the intended parity coverage without platform-dependent subtype iteration or a redundant default-lane pass.
4ec71932* Problem: Managed connection setup cannot join completion sets
* Problem: Managed connect lacks a selective-wait regression
The scoped construction tests use Wait_All, so they do not directly prove that a managed connect remains visible as one parent result while its hidden child is excluded from a heterogeneous success gate.
Solution: exercise managed connect with a timer success gate
Run the gate in both execution models, transfer the winning connection through typed Finish, cancel the timer sibling, and assert peer-close and admission cleanup.
4ec71932 → updated to c6b1bffcindex 980bb9e547 source commits
12eb5e12Problem: Benchmark confidence policy is fixeda9567c3bProblem: Bootstrap settings permit pathological analysis2a4f4862The statistical-policy changes alter the generated GNATdoc identity for Compare_Independent. The benchmarking guide and comparison journal retain the previous fragment, so the Pages link checker rejects the site built for PR #62.
Solution: Point both links at the regenerated entity anchor
Update the two deep links to the fragment emitted for Compare_Independent by the current flyology_bench API documentation.
98f7201aCurrent main widens the benchmark smoke counter to Unsigned_64 so fast hosts cannot overflow it. The statistical-policy layer snapshots that counter as Natural, making the rebased stack fail compilation before any benchmark runs.
Solution: Match the snapshot to the widened counter
Store the pre-execution snapshot as Interfaces.Unsigned_64 so assignment and comparison remain type-correct across the counter full range.
1c7a45e4The statistical smoke compares two live timing medians against a fixed fourfold ratio. Very fast operations can calibrate into dissimilar observed slices under scheduler and timer noise even though both slices are valid, causing intermittent Program_Error before the benchmark API is exercised further.
Solution: Validate timed-slice validity instead of proximity
Require both retained median batch durations to be positive. The smoke still verifies that equal-time comparison produced timed samples and separately verifies the configured batching policy, without treating host timing noise as a correctness failure.
f628b406The retake and pause checks require a helper process to cross a tick-based host CPU counter window. Runner scheduling can leave no actionable contaminated window, so the test faults even though the harness reports a coherent observation.
Solution: Validate responses only when the host observes them
Use tick-sized interference samples and windows, retain bounded response and sample-count checks, and require pause counters, elapsed time, and retaken samples to agree. A run with no actionable external window is now a valid integration outcome.
713783bfAdd extensible benchmark metrics and manual timingcb1c0c6bFix custom metric comparison reporting1be316cbHarden manual timing adapters340086a7Preserve missing custom probe failurescc7a3938Custom metric summaries and comparisons retain fixed bootstrap sizes and 95 percent interval tails after configurable statistical policy is introduced. Their additional intervals also remain outside the aggregate bootstrap-work ceiling.
Solution: Apply the result's statistical policy to custom metrics
Size custom bootstrap distributions from the configured resample count, derive their interval tails from the configured confidence level, and include every registered custom metric in bootstrap-work validation.
e9100447Add saved baseline regression gates4c4382afFix baseline gate compatibility and reporting8aba46e3Prove baseline regression arithmetic boundsfedeeb77The regression gate fixes every independent comparison at 95 percent confidence and 2,000 resamples even when callers use another benchmark policy. This also leaves the gate's bootstrap work outside the shared safety ceiling.
Solution: Make gate statistics explicit and bounded
Add source-compatible defaulted confidence and resample parameters to gate evaluation, propagate them through comparison and result metadata, validate aggregate bootstrap work, and cover non-default policy retention.
0b9f10beThe saved and current inputs reuse one live measurement, but the independent bootstrap resamples them separately. Scheduler outliers can widen the interval beyond the fixed threshold, causing valid inconclusive results to fail the benchmark test intermittently on CI and locally.
Solution: Exercise gate policy with deterministic samples
Add a test-only child helper that constructs a private measurement from a fixed mildly dispersed vector. Use that measurement for equivalence, boundary, inconclusive, and reporter coverage while retaining live measurements for collection and coarse improvement/regression smoke checks.
de70616eThe Ratio and Time_Change postconditions leave nonlinear floating-point bounds in combined prover searches. GNATprove 16.1.0 can reach its one-second level-one prover limit on Ubuntu CI even though repeated proofs may pass locally.
Solution: Split the floating bounds into stable proof obligations
State Ratio bounds directly in its postcondition and retain the exact Time_Change arithmetic while asserting each difficult bound in its matching branch. The final postcondition becomes a cheap control-flow merge without raising the global proof level or changing returned values.
8b189587Ubuntu GNATprove 16.1.0 still reaches the default one-second limit on the remaining Ratio monotonicity proof and the faster Time_Change bound. Further structural splits relocate the same floating-point search without reducing it.
Solution: Give only the benchmark proof a two-second timeout
Keep level 1 and every other proof invocation unchanged while extending the already-isolated baseline math proof budget. Record the focused budget and its rationale in the benchmark proof status.
7406db05Ubuntu GNATprove 16.1.0 can still exhaust the isolated two-second timeout on the Ratio lower-bound postcondition after the structural proof split. This leaves otherwise valid stack heads intermittently red.
Solution: Widen only the benchmark proof timeout
Increase the Baseline_Math timeout to five seconds while retaining level 1 and every other proof budget. Update the proof-status record to match the executable invocation.
739287caAdd benchmark parameter sweeps and scaling analysisce31f2e2Fix sweep availability and scaling metadata449eff4bFix sweep availability and display reportingdbf06156Fix sweep failure and scaling states03d277e6Fix exact sweep boundaries and reportingac1a8781Test final sweep budget stop semanticscee1d4abAdd explicit flyology_bench suite runner5334042aFix benchmark suite reporting and executione1433649Adding a defaulted context formal lets ordinary calls compile but breaks source uses that depend on the exact legacy reporter profile, including access attributes, renamings, and generic actuals. Ada also forbids overloading generic units.
Solution: Preserve legacy profiles beside contextual reporters
Restore exact pre-suite reporter signatures as forwarding entry points, expose contextual overloads for ordinary procedures and distinctly named contextual multi-way generics, update suite bindings, and compile-check access and renaming compatibility.
b5f078d4The suite aggregate reserves a regression-rejected outcome but registered measurements cannot evaluate a saved baseline. Users must duplicate execution and reporting logic outside the runner, and the suite cannot propagate a gate failure to its final status.
Solution: Add explicit read-only gated registrations
Let ordinary cases opt into a saved-baseline policy, evaluate it with the suite's effective confidence, resample, and seed settings after collection, report the gate in every output style, and give rejection precedence in the aggregate without creating or updating artifacts.
a062b019The GNATdoc workaround accepts any generic-formal warning from several benchmark units, so a newly undocumented public formal can pass documentation verification unnoticed.
Solution: Match only known GNATdoc false positives
Split the benchmark-unit filters and enumerate the exact root, reporter, sweep, and suite generic formal names that GNATdoc 26 misclassifies while continuing to reject every other warning.
962e5a48Hosted-runner memory faults currently end with only a runtime Storage_Error location. The local action log cannot identify which benchmark or reporter frame raised it, so platform-only failures are not actionable from CI evidence.
Solution: Capture symbolic test exception tracebacks
Build test mains with debug information and GNAT symbolic exception traceback support. On smoke failure, emit the complete exception occurrence to standard error before preserving the original unhandled failure.
a637cc13The reporting-failure scenario closes the caller's Ada.Text_IO file from inside a benchmark callback while Execute still holds and uses that file. GNAT platforms may retain a dangling internal file view, so the subsequent CSV header write can raise erroneous-memory-access instead of the Status_Error the test expects.
Solution: Keep runner streams valid for the execution lifetime
Remove the invalid cross-callback close scenario and document that Output and Progress must remain open and must not be reset or closed until Execute returns. Existing callback-failure tests continue to cover runner failure accounting without invoking dangling Text_IO behavior.
6f2cf34cAdd isolated benchmark worker processes8a195f6dHarden isolated benchmark worker containment2fb07b32Harden benchmark worker ownership validationc537aff4Harden benchmark worker result validationafb23533Protect benchmark worker environment metadataa29271b3Harden benchmark worker metadata validation3d0ce30aFresh-process executables must currently reconstruct case selection beside the suite registry, so the parent identity and worker callback can drift. Readiness and result return are also repeated in each worker main.
Solution: Execute worker requests through the suite registry
Validate the requested identity and result kind against an exact registration, rebuild worker-local configuration, announce readiness, invoke Execute_One, and return the matching protocol result. Update the maintained example to use this shared dispatch path.
c9c465f1Add fresh-process benchmark workers
6b7273a1Flyology and its standalone subcrates lack one enforced line-length and encoding policy. GNAT-preprocessed units also require a documented path that preserves every conditional branch.
Solution: Standardize Ada formatting at 110 columns
Configure each owning project for 110-column UTF-8 GNATformat output and matching compiler checks, align runtime compilation, document the preprocessing workflow, and reformat all tracked handwritten Ada sources.
ccf08b27GNATformat skips Ada units containing preprocessing directives, while the former test-hook switches made production code generation depend on a preprocessing pass. The repository also lacked an isolated regression proving that every hook selection is correct and that disabled calls disappear at all supported optimization levels.
Solution: Select hook packages through GPR source directories
Replace Ada directives with documented enabled and disabled private package variants, guard hook references with literal constants, and expose imported sentinels for missed guards. Add a dedicated cross-platform CI job that checks all 256 selections, optimization modes, and production archives without adding the exhaustive pass to the ordinary test suite.
edf9d8c0GNAT's runtime style checker rejects a formatter-shifted comment in the Linux file engine, preventing runtime assembly. GNATformat also separates three leading GNATdoc @exclude tags from their declarations, causing the Pages build to report those internal adapter instances as undocumented public API.
Solution: Restore validation-sensitive comment placement
Place the runtime comment in the GNAT style column, keep each @exclude tag adjacent to its declaration, and document the GNATformat exception in the agent guide.
2987d103An owner-stack connection protocol can arm hidden transport and lifecycle readiness through a Capability, but it cannot include the opaque Outbound_Wakeup used by concurrent stream publishers. Multiplexed clients therefore lack one bounded wait set and would need a helper pump or exposed descriptor.
Solution: compose outbound publication with transport arming
Add the narrow Arm_Transport overload that arms the required hidden transport direction, outbound wake source, and lifecycle sources on one outer operation. Consume already-pending notifications before rescheduling to prevent stale-signal spin, preserve independent deadlines, and cover pending, later-signal, reuse, TLS, deadline, native, and lightweight paths.
3815cc9dEstablished stream and datagram operations can block on transport readiness, but most scoped stream calls and Receive_Datagram cannot include caller-owned cancellation or protocol-output descriptors. DNS, HTTP/2, and HTTP/3 parents therefore cannot wake hidden socket work without a helper.
Solution: add bounded interrupt sets to scoped socket operations
Add source-compatible Interrupts parameters to scoped stream array operations and datagram receive roots, copy the bounded descriptor set into caller-owned operation storage, and arm it with transport readiness. Retain typed interruption and device failures and cover already-pending and later-signalled datagram interrupts in native and lightweight lanes.
ebf80d6bFlyology.IO.DNS exposes only synchronous Resolve calls, so an HTTP parent cannot preserve one owner-stack deadline and cancellation scope while resolving an origin. Wrapping the synchronous resolver would block a lightweight event-loop pthread and cannot participate in Continue_After or completion gates.
Solution: implement a bounded scoped DNS resolver state machine
Add reusable resolver configuration snapshots, limited Resolve operations with root and established starts, and typed Finish parity. Drive numeric, localhost, cache, search, family, retry, UDP, TCP fallback, token, deadline, and cancellation paths through hidden scoped socket children, with transactional capacity rollback and complete drain-before-publication tests in both lanes.
c6b1bffcAdd composable scoped DNS resolution
c6b1bffc → updated to 417e48adindex f41acb682 source commits
56a47436HTTP multiplexing needs one operation to wait on hidden transport, outbound,
lifecycle, token, and caller-source descriptors. The four-source bound overflows
H2, and datagram receives cannot express a direction-sensitive caller source
for H3.
Solution: Raise readiness capacity and add caller-source arming overloads
Raise the operation, public I/O, and runtime limits to six interests per
operation. Add connection and datagram overloads that include one validated,
caller-borrowed read-or-write descriptor in the existing bounded wait, retain
the outbound consume-before-reschedule rule, and cover native and lightweight
readiness, race, deadline, cancellation, and reuse behavior.
417e48adPreserve the reviewed Problem/Solution prerequisite commit and merge it after the complete Linux/macOS, SPARK, TLA+, and Pages matrix passed.
0.1.0-devCaller-owned memory allocation algorithms for Ada
ef6841e7 → updated to 4ec71932index 73e6c1b57 source commits
c9ccf71bFlyology lacks a generation protocol, listener-capability handoff, and coordinated canary lifecycle for replacing a running server with a changed executable. Operators cannot prepare, admit, promote, cancel, drain, fence, or reverse generations through one reviewed ownership model.
Solution: Add supervised process-generation upgrades and executable guide
Implement the Ada-owned coordinator and agent protocol, narrow descriptor and bootstrap handoff mechanisms, lifecycle policy, ABI and behavioral checks, and a mechanically extracted two-artifact website guide that exercises promotion and cancellation.
1fb1025eReaders can follow coordinator calls without knowing what behavior changes between the two artifacts or what the verification request proves.
Solution: Explain the example server and its observable cutover
Describe the one-byte request/reply behavior, the distinct generation replies, and how promotion and cancellation assertions relate to new connections.
5ec82af6Add supervised process-generation upgrades
270984a3The standalone flyology_bench Alire action captures command output in alire/alr_test_local.log. Hosted failures currently retain only Alire’s generic wrapper, so maintainers cannot identify which redirected smoke executable or assertion failed.
Solution: Surface benchmark test diagnostics on failure
Print the tail of the captured Alire log while preserving the original status, and label the two redirected smoke executables so the retained log names the active command. Successful test output remains unchanged apart from the command labels.
0081ba78The flyology_bench smoke fixture accumulates every calibrated operation in a Natural. Fast x86-64 runners can execute more than Natural Last operations across the suite, raising Constraint_Error before the runtime-specific tests begin.
Solution: Use a modular 64-bit operation counter
Store the volatile sink and scheduler-probe value directly as Interfaces.Unsigned_64. The counter now retains the intended observable work signal without a checked 32-bit accumulation limit.
04ff6dcd* Problem: Managed clients require raw socket adoption boilerplate
Composable protocol clients need a managed Flyology connection before they can use the public connection driver, but callers must create and connect a raw socket and then transfer it through Take. This obscures the admission, deadline, cancellation, and cleanup boundary.
Solution: Add a direct managed connection path
Add Connections.Connect to reserve admission, create and connect the endpoint-family socket under one deadline, and atomically adopt it with existing guards. Cover both lanes, failure cleanup, TLS and driver composition, and document raw and managed client choices.
* Problem: The managed connection test iterates a platform type
Execution_Model is discrete on Darwin but an access type on Linux, so iterating its subtype prevents the new smoke test from compiling on a supported platform. The Darwin loop also repeats the configured project default rather than naming the two lanes under test.
Solution: Run the two execution lanes explicitly
Move the scenarios into Run_All and invoke it once for Lightweight_Task and once for Native_Task. This preserves the intended parity coverage without platform-dependent subtype iteration or a redundant default-lane pass.
4ec71932* Problem: Managed connection setup cannot join completion sets
* Problem: Managed connect lacks a selective-wait regression
The scoped construction tests use Wait_All, so they do not directly prove that a managed connect remains visible as one parent result while its hidden child is excluded from a heterogeneous success gate.
Solution: exercise managed connect with a timer success gate
Run the gate in both execution models, transfer the winning connection through typed Finish, cancel the timer sibling, and assert peer-close and admission cleanup.
4ec71932 → updated to c6b1bffcindex 980bb9e547 source commits
12eb5e12Problem: Benchmark confidence policy is fixeda9567c3bProblem: Bootstrap settings permit pathological analysis2a4f4862The statistical-policy changes alter the generated GNATdoc identity for Compare_Independent. The benchmarking guide and comparison journal retain the previous fragment, so the Pages link checker rejects the site built for PR #62.
Solution: Point both links at the regenerated entity anchor
Update the two deep links to the fragment emitted for Compare_Independent by the current flyology_bench API documentation.
98f7201aCurrent main widens the benchmark smoke counter to Unsigned_64 so fast hosts cannot overflow it. The statistical-policy layer snapshots that counter as Natural, making the rebased stack fail compilation before any benchmark runs.
Solution: Match the snapshot to the widened counter
Store the pre-execution snapshot as Interfaces.Unsigned_64 so assignment and comparison remain type-correct across the counter full range.
1c7a45e4The statistical smoke compares two live timing medians against a fixed fourfold ratio. Very fast operations can calibrate into dissimilar observed slices under scheduler and timer noise even though both slices are valid, causing intermittent Program_Error before the benchmark API is exercised further.
Solution: Validate timed-slice validity instead of proximity
Require both retained median batch durations to be positive. The smoke still verifies that equal-time comparison produced timed samples and separately verifies the configured batching policy, without treating host timing noise as a correctness failure.
f628b406The retake and pause checks require a helper process to cross a tick-based host CPU counter window. Runner scheduling can leave no actionable contaminated window, so the test faults even though the harness reports a coherent observation.
Solution: Validate responses only when the host observes them
Use tick-sized interference samples and windows, retain bounded response and sample-count checks, and require pause counters, elapsed time, and retaken samples to agree. A run with no actionable external window is now a valid integration outcome.
713783bfAdd extensible benchmark metrics and manual timingcb1c0c6bFix custom metric comparison reporting1be316cbHarden manual timing adapters340086a7Preserve missing custom probe failurescc7a3938Custom metric summaries and comparisons retain fixed bootstrap sizes and 95 percent interval tails after configurable statistical policy is introduced. Their additional intervals also remain outside the aggregate bootstrap-work ceiling.
Solution: Apply the result's statistical policy to custom metrics
Size custom bootstrap distributions from the configured resample count, derive their interval tails from the configured confidence level, and include every registered custom metric in bootstrap-work validation.
e9100447Add saved baseline regression gates4c4382afFix baseline gate compatibility and reporting8aba46e3Prove baseline regression arithmetic boundsfedeeb77The regression gate fixes every independent comparison at 95 percent confidence and 2,000 resamples even when callers use another benchmark policy. This also leaves the gate's bootstrap work outside the shared safety ceiling.
Solution: Make gate statistics explicit and bounded
Add source-compatible defaulted confidence and resample parameters to gate evaluation, propagate them through comparison and result metadata, validate aggregate bootstrap work, and cover non-default policy retention.
0b9f10beThe saved and current inputs reuse one live measurement, but the independent bootstrap resamples them separately. Scheduler outliers can widen the interval beyond the fixed threshold, causing valid inconclusive results to fail the benchmark test intermittently on CI and locally.
Solution: Exercise gate policy with deterministic samples
Add a test-only child helper that constructs a private measurement from a fixed mildly dispersed vector. Use that measurement for equivalence, boundary, inconclusive, and reporter coverage while retaining live measurements for collection and coarse improvement/regression smoke checks.
de70616eThe Ratio and Time_Change postconditions leave nonlinear floating-point bounds in combined prover searches. GNATprove 16.1.0 can reach its one-second level-one prover limit on Ubuntu CI even though repeated proofs may pass locally.
Solution: Split the floating bounds into stable proof obligations
State Ratio bounds directly in its postcondition and retain the exact Time_Change arithmetic while asserting each difficult bound in its matching branch. The final postcondition becomes a cheap control-flow merge without raising the global proof level or changing returned values.
8b189587Ubuntu GNATprove 16.1.0 still reaches the default one-second limit on the remaining Ratio monotonicity proof and the faster Time_Change bound. Further structural splits relocate the same floating-point search without reducing it.
Solution: Give only the benchmark proof a two-second timeout
Keep level 1 and every other proof invocation unchanged while extending the already-isolated baseline math proof budget. Record the focused budget and its rationale in the benchmark proof status.
7406db05Ubuntu GNATprove 16.1.0 can still exhaust the isolated two-second timeout on the Ratio lower-bound postcondition after the structural proof split. This leaves otherwise valid stack heads intermittently red.
Solution: Widen only the benchmark proof timeout
Increase the Baseline_Math timeout to five seconds while retaining level 1 and every other proof budget. Update the proof-status record to match the executable invocation.
739287caAdd benchmark parameter sweeps and scaling analysisce31f2e2Fix sweep availability and scaling metadata449eff4bFix sweep availability and display reportingdbf06156Fix sweep failure and scaling states03d277e6Fix exact sweep boundaries and reportingac1a8781Test final sweep budget stop semanticscee1d4abAdd explicit flyology_bench suite runner5334042aFix benchmark suite reporting and executione1433649Adding a defaulted context formal lets ordinary calls compile but breaks source uses that depend on the exact legacy reporter profile, including access attributes, renamings, and generic actuals. Ada also forbids overloading generic units.
Solution: Preserve legacy profiles beside contextual reporters
Restore exact pre-suite reporter signatures as forwarding entry points, expose contextual overloads for ordinary procedures and distinctly named contextual multi-way generics, update suite bindings, and compile-check access and renaming compatibility.
b5f078d4The suite aggregate reserves a regression-rejected outcome but registered measurements cannot evaluate a saved baseline. Users must duplicate execution and reporting logic outside the runner, and the suite cannot propagate a gate failure to its final status.
Solution: Add explicit read-only gated registrations
Let ordinary cases opt into a saved-baseline policy, evaluate it with the suite's effective confidence, resample, and seed settings after collection, report the gate in every output style, and give rejection precedence in the aggregate without creating or updating artifacts.
a062b019The GNATdoc workaround accepts any generic-formal warning from several benchmark units, so a newly undocumented public formal can pass documentation verification unnoticed.
Solution: Match only known GNATdoc false positives
Split the benchmark-unit filters and enumerate the exact root, reporter, sweep, and suite generic formal names that GNATdoc 26 misclassifies while continuing to reject every other warning.
962e5a48Hosted-runner memory faults currently end with only a runtime Storage_Error location. The local action log cannot identify which benchmark or reporter frame raised it, so platform-only failures are not actionable from CI evidence.
Solution: Capture symbolic test exception tracebacks
Build test mains with debug information and GNAT symbolic exception traceback support. On smoke failure, emit the complete exception occurrence to standard error before preserving the original unhandled failure.
a637cc13The reporting-failure scenario closes the caller's Ada.Text_IO file from inside a benchmark callback while Execute still holds and uses that file. GNAT platforms may retain a dangling internal file view, so the subsequent CSV header write can raise erroneous-memory-access instead of the Status_Error the test expects.
Solution: Keep runner streams valid for the execution lifetime
Remove the invalid cross-callback close scenario and document that Output and Progress must remain open and must not be reset or closed until Execute returns. Existing callback-failure tests continue to cover runner failure accounting without invoking dangling Text_IO behavior.
6f2cf34cAdd isolated benchmark worker processes8a195f6dHarden isolated benchmark worker containment2fb07b32Harden benchmark worker ownership validationc537aff4Harden benchmark worker result validationafb23533Protect benchmark worker environment metadataa29271b3Harden benchmark worker metadata validation3d0ce30aFresh-process executables must currently reconstruct case selection beside the suite registry, so the parent identity and worker callback can drift. Readiness and result return are also repeated in each worker main.
Solution: Execute worker requests through the suite registry
Validate the requested identity and result kind against an exact registration, rebuild worker-local configuration, announce readiness, invoke Execute_One, and return the matching protocol result. Update the maintained example to use this shared dispatch path.
c9c465f1Add fresh-process benchmark workers
6b7273a1Flyology and its standalone subcrates lack one enforced line-length and encoding policy. GNAT-preprocessed units also require a documented path that preserves every conditional branch.
Solution: Standardize Ada formatting at 110 columns
Configure each owning project for 110-column UTF-8 GNATformat output and matching compiler checks, align runtime compilation, document the preprocessing workflow, and reformat all tracked handwritten Ada sources.
ccf08b27GNATformat skips Ada units containing preprocessing directives, while the former test-hook switches made production code generation depend on a preprocessing pass. The repository also lacked an isolated regression proving that every hook selection is correct and that disabled calls disappear at all supported optimization levels.
Solution: Select hook packages through GPR source directories
Replace Ada directives with documented enabled and disabled private package variants, guard hook references with literal constants, and expose imported sentinels for missed guards. Add a dedicated cross-platform CI job that checks all 256 selections, optimization modes, and production archives without adding the exhaustive pass to the ordinary test suite.
edf9d8c0GNAT's runtime style checker rejects a formatter-shifted comment in the Linux file engine, preventing runtime assembly. GNATformat also separates three leading GNATdoc @exclude tags from their declarations, causing the Pages build to report those internal adapter instances as undocumented public API.
Solution: Restore validation-sensitive comment placement
Place the runtime comment in the GNAT style column, keep each @exclude tag adjacent to its declaration, and document the GNATformat exception in the agent guide.
2987d103An owner-stack connection protocol can arm hidden transport and lifecycle readiness through a Capability, but it cannot include the opaque Outbound_Wakeup used by concurrent stream publishers. Multiplexed clients therefore lack one bounded wait set and would need a helper pump or exposed descriptor.
Solution: compose outbound publication with transport arming
Add the narrow Arm_Transport overload that arms the required hidden transport direction, outbound wake source, and lifecycle sources on one outer operation. Consume already-pending notifications before rescheduling to prevent stale-signal spin, preserve independent deadlines, and cover pending, later-signal, reuse, TLS, deadline, native, and lightweight paths.
3815cc9dEstablished stream and datagram operations can block on transport readiness, but most scoped stream calls and Receive_Datagram cannot include caller-owned cancellation or protocol-output descriptors. DNS, HTTP/2, and HTTP/3 parents therefore cannot wake hidden socket work without a helper.
Solution: add bounded interrupt sets to scoped socket operations
Add source-compatible Interrupts parameters to scoped stream array operations and datagram receive roots, copy the bounded descriptor set into caller-owned operation storage, and arm it with transport readiness. Retain typed interruption and device failures and cover already-pending and later-signalled datagram interrupts in native and lightweight lanes.
ebf80d6bFlyology.IO.DNS exposes only synchronous Resolve calls, so an HTTP parent cannot preserve one owner-stack deadline and cancellation scope while resolving an origin. Wrapping the synchronous resolver would block a lightweight event-loop pthread and cannot participate in Continue_After or completion gates.
Solution: implement a bounded scoped DNS resolver state machine
Add reusable resolver configuration snapshots, limited Resolve operations with root and established starts, and typed Finish parity. Drive numeric, localhost, cache, search, family, retry, UDP, TCP fallback, token, deadline, and cancellation paths through hidden scoped socket children, with transactional capacity rollback and complete drain-before-publication tests in both lanes.
c6b1bffcAdd composable scoped DNS resolution
c6b1bffc → updated to 417e48adindex f41acb682 source commits
56a47436HTTP multiplexing needs one operation to wait on hidden transport, outbound,
lifecycle, token, and caller-source descriptors. The four-source bound overflows
H2, and datagram receives cannot express a direction-sensitive caller source
for H3.
Solution: Raise readiness capacity and add caller-source arming overloads
Raise the operation, public I/O, and runtime limits to six interests per
operation. Add connection and datagram overloads that include one validated,
caller-borrowed read-or-write descriptor in the existing bounded wait, retain
the outbound consume-before-reschedule rule, and cover native and lightweight
readiness, race, deadline, cancellation, and reuse behavior.
417e48adPreserve the reviewed Problem/Solution prerequisite commit and merge it after the complete Linux/macOS, SPARK, TLA+, and Pages matrix passed.
0.1.1-devAdaptive nanosecond-scale benchmarking for Ada
ef6841e7 → updated to 4ec71932index 73e6c1b57 source commits
c9ccf71bFlyology lacks a generation protocol, listener-capability handoff, and coordinated canary lifecycle for replacing a running server with a changed executable. Operators cannot prepare, admit, promote, cancel, drain, fence, or reverse generations through one reviewed ownership model.
Solution: Add supervised process-generation upgrades and executable guide
Implement the Ada-owned coordinator and agent protocol, narrow descriptor and bootstrap handoff mechanisms, lifecycle policy, ABI and behavioral checks, and a mechanically extracted two-artifact website guide that exercises promotion and cancellation.
1fb1025eReaders can follow coordinator calls without knowing what behavior changes between the two artifacts or what the verification request proves.
Solution: Explain the example server and its observable cutover
Describe the one-byte request/reply behavior, the distinct generation replies, and how promotion and cancellation assertions relate to new connections.
5ec82af6Add supervised process-generation upgrades
270984a3The standalone flyology_bench Alire action captures command output in alire/alr_test_local.log. Hosted failures currently retain only Alire’s generic wrapper, so maintainers cannot identify which redirected smoke executable or assertion failed.
Solution: Surface benchmark test diagnostics on failure
Print the tail of the captured Alire log while preserving the original status, and label the two redirected smoke executables so the retained log names the active command. Successful test output remains unchanged apart from the command labels.
0081ba78The flyology_bench smoke fixture accumulates every calibrated operation in a Natural. Fast x86-64 runners can execute more than Natural Last operations across the suite, raising Constraint_Error before the runtime-specific tests begin.
Solution: Use a modular 64-bit operation counter
Store the volatile sink and scheduler-probe value directly as Interfaces.Unsigned_64. The counter now retains the intended observable work signal without a checked 32-bit accumulation limit.
04ff6dcd* Problem: Managed clients require raw socket adoption boilerplate
Composable protocol clients need a managed Flyology connection before they can use the public connection driver, but callers must create and connect a raw socket and then transfer it through Take. This obscures the admission, deadline, cancellation, and cleanup boundary.
Solution: Add a direct managed connection path
Add Connections.Connect to reserve admission, create and connect the endpoint-family socket under one deadline, and atomically adopt it with existing guards. Cover both lanes, failure cleanup, TLS and driver composition, and document raw and managed client choices.
* Problem: The managed connection test iterates a platform type
Execution_Model is discrete on Darwin but an access type on Linux, so iterating its subtype prevents the new smoke test from compiling on a supported platform. The Darwin loop also repeats the configured project default rather than naming the two lanes under test.
Solution: Run the two execution lanes explicitly
Move the scenarios into Run_All and invoke it once for Lightweight_Task and once for Native_Task. This preserves the intended parity coverage without platform-dependent subtype iteration or a redundant default-lane pass.
4ec71932* Problem: Managed connection setup cannot join completion sets
* Problem: Managed connect lacks a selective-wait regression
The scoped construction tests use Wait_All, so they do not directly prove that a managed connect remains visible as one parent result while its hidden child is excluded from a heterogeneous success gate.
Solution: exercise managed connect with a timer success gate
Run the gate in both execution models, transfer the winning connection through typed Finish, cancel the timer sibling, and assert peer-close and admission cleanup.
4ec71932 → updated to c6b1bffcindex 980bb9e547 source commits
12eb5e12Problem: Benchmark confidence policy is fixeda9567c3bProblem: Bootstrap settings permit pathological analysis2a4f4862The statistical-policy changes alter the generated GNATdoc identity for Compare_Independent. The benchmarking guide and comparison journal retain the previous fragment, so the Pages link checker rejects the site built for PR #62.
Solution: Point both links at the regenerated entity anchor
Update the two deep links to the fragment emitted for Compare_Independent by the current flyology_bench API documentation.
98f7201aCurrent main widens the benchmark smoke counter to Unsigned_64 so fast hosts cannot overflow it. The statistical-policy layer snapshots that counter as Natural, making the rebased stack fail compilation before any benchmark runs.
Solution: Match the snapshot to the widened counter
Store the pre-execution snapshot as Interfaces.Unsigned_64 so assignment and comparison remain type-correct across the counter full range.
1c7a45e4The statistical smoke compares two live timing medians against a fixed fourfold ratio. Very fast operations can calibrate into dissimilar observed slices under scheduler and timer noise even though both slices are valid, causing intermittent Program_Error before the benchmark API is exercised further.
Solution: Validate timed-slice validity instead of proximity
Require both retained median batch durations to be positive. The smoke still verifies that equal-time comparison produced timed samples and separately verifies the configured batching policy, without treating host timing noise as a correctness failure.
f628b406The retake and pause checks require a helper process to cross a tick-based host CPU counter window. Runner scheduling can leave no actionable contaminated window, so the test faults even though the harness reports a coherent observation.
Solution: Validate responses only when the host observes them
Use tick-sized interference samples and windows, retain bounded response and sample-count checks, and require pause counters, elapsed time, and retaken samples to agree. A run with no actionable external window is now a valid integration outcome.
713783bfAdd extensible benchmark metrics and manual timingcb1c0c6bFix custom metric comparison reporting1be316cbHarden manual timing adapters340086a7Preserve missing custom probe failurescc7a3938Custom metric summaries and comparisons retain fixed bootstrap sizes and 95 percent interval tails after configurable statistical policy is introduced. Their additional intervals also remain outside the aggregate bootstrap-work ceiling.
Solution: Apply the result's statistical policy to custom metrics
Size custom bootstrap distributions from the configured resample count, derive their interval tails from the configured confidence level, and include every registered custom metric in bootstrap-work validation.
e9100447Add saved baseline regression gates4c4382afFix baseline gate compatibility and reporting8aba46e3Prove baseline regression arithmetic boundsfedeeb77The regression gate fixes every independent comparison at 95 percent confidence and 2,000 resamples even when callers use another benchmark policy. This also leaves the gate's bootstrap work outside the shared safety ceiling.
Solution: Make gate statistics explicit and bounded
Add source-compatible defaulted confidence and resample parameters to gate evaluation, propagate them through comparison and result metadata, validate aggregate bootstrap work, and cover non-default policy retention.
0b9f10beThe saved and current inputs reuse one live measurement, but the independent bootstrap resamples them separately. Scheduler outliers can widen the interval beyond the fixed threshold, causing valid inconclusive results to fail the benchmark test intermittently on CI and locally.
Solution: Exercise gate policy with deterministic samples
Add a test-only child helper that constructs a private measurement from a fixed mildly dispersed vector. Use that measurement for equivalence, boundary, inconclusive, and reporter coverage while retaining live measurements for collection and coarse improvement/regression smoke checks.
de70616eThe Ratio and Time_Change postconditions leave nonlinear floating-point bounds in combined prover searches. GNATprove 16.1.0 can reach its one-second level-one prover limit on Ubuntu CI even though repeated proofs may pass locally.
Solution: Split the floating bounds into stable proof obligations
State Ratio bounds directly in its postcondition and retain the exact Time_Change arithmetic while asserting each difficult bound in its matching branch. The final postcondition becomes a cheap control-flow merge without raising the global proof level or changing returned values.
8b189587Ubuntu GNATprove 16.1.0 still reaches the default one-second limit on the remaining Ratio monotonicity proof and the faster Time_Change bound. Further structural splits relocate the same floating-point search without reducing it.
Solution: Give only the benchmark proof a two-second timeout
Keep level 1 and every other proof invocation unchanged while extending the already-isolated baseline math proof budget. Record the focused budget and its rationale in the benchmark proof status.
7406db05Ubuntu GNATprove 16.1.0 can still exhaust the isolated two-second timeout on the Ratio lower-bound postcondition after the structural proof split. This leaves otherwise valid stack heads intermittently red.
Solution: Widen only the benchmark proof timeout
Increase the Baseline_Math timeout to five seconds while retaining level 1 and every other proof budget. Update the proof-status record to match the executable invocation.
739287caAdd benchmark parameter sweeps and scaling analysisce31f2e2Fix sweep availability and scaling metadata449eff4bFix sweep availability and display reportingdbf06156Fix sweep failure and scaling states03d277e6Fix exact sweep boundaries and reportingac1a8781Test final sweep budget stop semanticscee1d4abAdd explicit flyology_bench suite runner5334042aFix benchmark suite reporting and executione1433649Adding a defaulted context formal lets ordinary calls compile but breaks source uses that depend on the exact legacy reporter profile, including access attributes, renamings, and generic actuals. Ada also forbids overloading generic units.
Solution: Preserve legacy profiles beside contextual reporters
Restore exact pre-suite reporter signatures as forwarding entry points, expose contextual overloads for ordinary procedures and distinctly named contextual multi-way generics, update suite bindings, and compile-check access and renaming compatibility.
b5f078d4The suite aggregate reserves a regression-rejected outcome but registered measurements cannot evaluate a saved baseline. Users must duplicate execution and reporting logic outside the runner, and the suite cannot propagate a gate failure to its final status.
Solution: Add explicit read-only gated registrations
Let ordinary cases opt into a saved-baseline policy, evaluate it with the suite's effective confidence, resample, and seed settings after collection, report the gate in every output style, and give rejection precedence in the aggregate without creating or updating artifacts.
a062b019The GNATdoc workaround accepts any generic-formal warning from several benchmark units, so a newly undocumented public formal can pass documentation verification unnoticed.
Solution: Match only known GNATdoc false positives
Split the benchmark-unit filters and enumerate the exact root, reporter, sweep, and suite generic formal names that GNATdoc 26 misclassifies while continuing to reject every other warning.
962e5a48Hosted-runner memory faults currently end with only a runtime Storage_Error location. The local action log cannot identify which benchmark or reporter frame raised it, so platform-only failures are not actionable from CI evidence.
Solution: Capture symbolic test exception tracebacks
Build test mains with debug information and GNAT symbolic exception traceback support. On smoke failure, emit the complete exception occurrence to standard error before preserving the original unhandled failure.
a637cc13The reporting-failure scenario closes the caller's Ada.Text_IO file from inside a benchmark callback while Execute still holds and uses that file. GNAT platforms may retain a dangling internal file view, so the subsequent CSV header write can raise erroneous-memory-access instead of the Status_Error the test expects.
Solution: Keep runner streams valid for the execution lifetime
Remove the invalid cross-callback close scenario and document that Output and Progress must remain open and must not be reset or closed until Execute returns. Existing callback-failure tests continue to cover runner failure accounting without invoking dangling Text_IO behavior.
6f2cf34cAdd isolated benchmark worker processes8a195f6dHarden isolated benchmark worker containment2fb07b32Harden benchmark worker ownership validationc537aff4Harden benchmark worker result validationafb23533Protect benchmark worker environment metadataa29271b3Harden benchmark worker metadata validation3d0ce30aFresh-process executables must currently reconstruct case selection beside the suite registry, so the parent identity and worker callback can drift. Readiness and result return are also repeated in each worker main.
Solution: Execute worker requests through the suite registry
Validate the requested identity and result kind against an exact registration, rebuild worker-local configuration, announce readiness, invoke Execute_One, and return the matching protocol result. Update the maintained example to use this shared dispatch path.
c9c465f1Add fresh-process benchmark workers
6b7273a1Flyology and its standalone subcrates lack one enforced line-length and encoding policy. GNAT-preprocessed units also require a documented path that preserves every conditional branch.
Solution: Standardize Ada formatting at 110 columns
Configure each owning project for 110-column UTF-8 GNATformat output and matching compiler checks, align runtime compilation, document the preprocessing workflow, and reformat all tracked handwritten Ada sources.
ccf08b27GNATformat skips Ada units containing preprocessing directives, while the former test-hook switches made production code generation depend on a preprocessing pass. The repository also lacked an isolated regression proving that every hook selection is correct and that disabled calls disappear at all supported optimization levels.
Solution: Select hook packages through GPR source directories
Replace Ada directives with documented enabled and disabled private package variants, guard hook references with literal constants, and expose imported sentinels for missed guards. Add a dedicated cross-platform CI job that checks all 256 selections, optimization modes, and production archives without adding the exhaustive pass to the ordinary test suite.
edf9d8c0GNAT's runtime style checker rejects a formatter-shifted comment in the Linux file engine, preventing runtime assembly. GNATformat also separates three leading GNATdoc @exclude tags from their declarations, causing the Pages build to report those internal adapter instances as undocumented public API.
Solution: Restore validation-sensitive comment placement
Place the runtime comment in the GNAT style column, keep each @exclude tag adjacent to its declaration, and document the GNATformat exception in the agent guide.
2987d103An owner-stack connection protocol can arm hidden transport and lifecycle readiness through a Capability, but it cannot include the opaque Outbound_Wakeup used by concurrent stream publishers. Multiplexed clients therefore lack one bounded wait set and would need a helper pump or exposed descriptor.
Solution: compose outbound publication with transport arming
Add the narrow Arm_Transport overload that arms the required hidden transport direction, outbound wake source, and lifecycle sources on one outer operation. Consume already-pending notifications before rescheduling to prevent stale-signal spin, preserve independent deadlines, and cover pending, later-signal, reuse, TLS, deadline, native, and lightweight paths.
3815cc9dEstablished stream and datagram operations can block on transport readiness, but most scoped stream calls and Receive_Datagram cannot include caller-owned cancellation or protocol-output descriptors. DNS, HTTP/2, and HTTP/3 parents therefore cannot wake hidden socket work without a helper.
Solution: add bounded interrupt sets to scoped socket operations
Add source-compatible Interrupts parameters to scoped stream array operations and datagram receive roots, copy the bounded descriptor set into caller-owned operation storage, and arm it with transport readiness. Retain typed interruption and device failures and cover already-pending and later-signalled datagram interrupts in native and lightweight lanes.
ebf80d6bFlyology.IO.DNS exposes only synchronous Resolve calls, so an HTTP parent cannot preserve one owner-stack deadline and cancellation scope while resolving an origin. Wrapping the synchronous resolver would block a lightweight event-loop pthread and cannot participate in Continue_After or completion gates.
Solution: implement a bounded scoped DNS resolver state machine
Add reusable resolver configuration snapshots, limited Resolve operations with root and established starts, and typed Finish parity. Drive numeric, localhost, cache, search, family, retry, UDP, TCP fallback, token, deadline, and cancellation paths through hidden scoped socket children, with transactional capacity rollback and complete drain-before-publication tests in both lanes.
c6b1bffcAdd composable scoped DNS resolution
c6b1bffc → updated to 417e48adindex f41acb682 source commits
56a47436HTTP multiplexing needs one operation to wait on hidden transport, outbound,
lifecycle, token, and caller-source descriptors. The four-source bound overflows
H2, and datagram receives cannot express a direction-sensitive caller source
for H3.
Solution: Raise readiness capacity and add caller-source arming overloads
Raise the operation, public I/O, and runtime limits to six interests per
operation. Add connection and datagram overloads that include one validated,
caller-borrowed read-or-write descriptor in the existing bounded wait, retain
the outbound consume-before-reschedule rule, and cover native and lightweight
readiness, race, deadline, cancellation, and reuse behavior.
417e48adPreserve the reviewed Problem/Solution prerequisite commit and merge it after the complete Linux/macOS, SPARK, TLA+, and Pages matrix passed.
0.1.1-devCache-line-aware storage and cache information for Ada
ef6841e7 → updated to 4ec71932index 73e6c1b57 source commits
c9ccf71bFlyology lacks a generation protocol, listener-capability handoff, and coordinated canary lifecycle for replacing a running server with a changed executable. Operators cannot prepare, admit, promote, cancel, drain, fence, or reverse generations through one reviewed ownership model.
Solution: Add supervised process-generation upgrades and executable guide
Implement the Ada-owned coordinator and agent protocol, narrow descriptor and bootstrap handoff mechanisms, lifecycle policy, ABI and behavioral checks, and a mechanically extracted two-artifact website guide that exercises promotion and cancellation.
1fb1025eReaders can follow coordinator calls without knowing what behavior changes between the two artifacts or what the verification request proves.
Solution: Explain the example server and its observable cutover
Describe the one-byte request/reply behavior, the distinct generation replies, and how promotion and cancellation assertions relate to new connections.
5ec82af6Add supervised process-generation upgrades
270984a3The standalone flyology_bench Alire action captures command output in alire/alr_test_local.log. Hosted failures currently retain only Alire’s generic wrapper, so maintainers cannot identify which redirected smoke executable or assertion failed.
Solution: Surface benchmark test diagnostics on failure
Print the tail of the captured Alire log while preserving the original status, and label the two redirected smoke executables so the retained log names the active command. Successful test output remains unchanged apart from the command labels.
0081ba78The flyology_bench smoke fixture accumulates every calibrated operation in a Natural. Fast x86-64 runners can execute more than Natural Last operations across the suite, raising Constraint_Error before the runtime-specific tests begin.
Solution: Use a modular 64-bit operation counter
Store the volatile sink and scheduler-probe value directly as Interfaces.Unsigned_64. The counter now retains the intended observable work signal without a checked 32-bit accumulation limit.
04ff6dcd* Problem: Managed clients require raw socket adoption boilerplate
Composable protocol clients need a managed Flyology connection before they can use the public connection driver, but callers must create and connect a raw socket and then transfer it through Take. This obscures the admission, deadline, cancellation, and cleanup boundary.
Solution: Add a direct managed connection path
Add Connections.Connect to reserve admission, create and connect the endpoint-family socket under one deadline, and atomically adopt it with existing guards. Cover both lanes, failure cleanup, TLS and driver composition, and document raw and managed client choices.
* Problem: The managed connection test iterates a platform type
Execution_Model is discrete on Darwin but an access type on Linux, so iterating its subtype prevents the new smoke test from compiling on a supported platform. The Darwin loop also repeats the configured project default rather than naming the two lanes under test.
Solution: Run the two execution lanes explicitly
Move the scenarios into Run_All and invoke it once for Lightweight_Task and once for Native_Task. This preserves the intended parity coverage without platform-dependent subtype iteration or a redundant default-lane pass.
4ec71932* Problem: Managed connection setup cannot join completion sets
* Problem: Managed connect lacks a selective-wait regression
The scoped construction tests use Wait_All, so they do not directly prove that a managed connect remains visible as one parent result while its hidden child is excluded from a heterogeneous success gate.
Solution: exercise managed connect with a timer success gate
Run the gate in both execution models, transfer the winning connection through typed Finish, cancel the timer sibling, and assert peer-close and admission cleanup.
4ec71932 → updated to c6b1bffcindex 980bb9e547 source commits
12eb5e12Problem: Benchmark confidence policy is fixeda9567c3bProblem: Bootstrap settings permit pathological analysis2a4f4862The statistical-policy changes alter the generated GNATdoc identity for Compare_Independent. The benchmarking guide and comparison journal retain the previous fragment, so the Pages link checker rejects the site built for PR #62.
Solution: Point both links at the regenerated entity anchor
Update the two deep links to the fragment emitted for Compare_Independent by the current flyology_bench API documentation.
98f7201aCurrent main widens the benchmark smoke counter to Unsigned_64 so fast hosts cannot overflow it. The statistical-policy layer snapshots that counter as Natural, making the rebased stack fail compilation before any benchmark runs.
Solution: Match the snapshot to the widened counter
Store the pre-execution snapshot as Interfaces.Unsigned_64 so assignment and comparison remain type-correct across the counter full range.
1c7a45e4The statistical smoke compares two live timing medians against a fixed fourfold ratio. Very fast operations can calibrate into dissimilar observed slices under scheduler and timer noise even though both slices are valid, causing intermittent Program_Error before the benchmark API is exercised further.
Solution: Validate timed-slice validity instead of proximity
Require both retained median batch durations to be positive. The smoke still verifies that equal-time comparison produced timed samples and separately verifies the configured batching policy, without treating host timing noise as a correctness failure.
f628b406The retake and pause checks require a helper process to cross a tick-based host CPU counter window. Runner scheduling can leave no actionable contaminated window, so the test faults even though the harness reports a coherent observation.
Solution: Validate responses only when the host observes them
Use tick-sized interference samples and windows, retain bounded response and sample-count checks, and require pause counters, elapsed time, and retaken samples to agree. A run with no actionable external window is now a valid integration outcome.
713783bfAdd extensible benchmark metrics and manual timingcb1c0c6bFix custom metric comparison reporting1be316cbHarden manual timing adapters340086a7Preserve missing custom probe failurescc7a3938Custom metric summaries and comparisons retain fixed bootstrap sizes and 95 percent interval tails after configurable statistical policy is introduced. Their additional intervals also remain outside the aggregate bootstrap-work ceiling.
Solution: Apply the result's statistical policy to custom metrics
Size custom bootstrap distributions from the configured resample count, derive their interval tails from the configured confidence level, and include every registered custom metric in bootstrap-work validation.
e9100447Add saved baseline regression gates4c4382afFix baseline gate compatibility and reporting8aba46e3Prove baseline regression arithmetic boundsfedeeb77The regression gate fixes every independent comparison at 95 percent confidence and 2,000 resamples even when callers use another benchmark policy. This also leaves the gate's bootstrap work outside the shared safety ceiling.
Solution: Make gate statistics explicit and bounded
Add source-compatible defaulted confidence and resample parameters to gate evaluation, propagate them through comparison and result metadata, validate aggregate bootstrap work, and cover non-default policy retention.
0b9f10beThe saved and current inputs reuse one live measurement, but the independent bootstrap resamples them separately. Scheduler outliers can widen the interval beyond the fixed threshold, causing valid inconclusive results to fail the benchmark test intermittently on CI and locally.
Solution: Exercise gate policy with deterministic samples
Add a test-only child helper that constructs a private measurement from a fixed mildly dispersed vector. Use that measurement for equivalence, boundary, inconclusive, and reporter coverage while retaining live measurements for collection and coarse improvement/regression smoke checks.
de70616eThe Ratio and Time_Change postconditions leave nonlinear floating-point bounds in combined prover searches. GNATprove 16.1.0 can reach its one-second level-one prover limit on Ubuntu CI even though repeated proofs may pass locally.
Solution: Split the floating bounds into stable proof obligations
State Ratio bounds directly in its postcondition and retain the exact Time_Change arithmetic while asserting each difficult bound in its matching branch. The final postcondition becomes a cheap control-flow merge without raising the global proof level or changing returned values.
8b189587Ubuntu GNATprove 16.1.0 still reaches the default one-second limit on the remaining Ratio monotonicity proof and the faster Time_Change bound. Further structural splits relocate the same floating-point search without reducing it.
Solution: Give only the benchmark proof a two-second timeout
Keep level 1 and every other proof invocation unchanged while extending the already-isolated baseline math proof budget. Record the focused budget and its rationale in the benchmark proof status.
7406db05Ubuntu GNATprove 16.1.0 can still exhaust the isolated two-second timeout on the Ratio lower-bound postcondition after the structural proof split. This leaves otherwise valid stack heads intermittently red.
Solution: Widen only the benchmark proof timeout
Increase the Baseline_Math timeout to five seconds while retaining level 1 and every other proof budget. Update the proof-status record to match the executable invocation.
739287caAdd benchmark parameter sweeps and scaling analysisce31f2e2Fix sweep availability and scaling metadata449eff4bFix sweep availability and display reportingdbf06156Fix sweep failure and scaling states03d277e6Fix exact sweep boundaries and reportingac1a8781Test final sweep budget stop semanticscee1d4abAdd explicit flyology_bench suite runner5334042aFix benchmark suite reporting and executione1433649Adding a defaulted context formal lets ordinary calls compile but breaks source uses that depend on the exact legacy reporter profile, including access attributes, renamings, and generic actuals. Ada also forbids overloading generic units.
Solution: Preserve legacy profiles beside contextual reporters
Restore exact pre-suite reporter signatures as forwarding entry points, expose contextual overloads for ordinary procedures and distinctly named contextual multi-way generics, update suite bindings, and compile-check access and renaming compatibility.
b5f078d4The suite aggregate reserves a regression-rejected outcome but registered measurements cannot evaluate a saved baseline. Users must duplicate execution and reporting logic outside the runner, and the suite cannot propagate a gate failure to its final status.
Solution: Add explicit read-only gated registrations
Let ordinary cases opt into a saved-baseline policy, evaluate it with the suite's effective confidence, resample, and seed settings after collection, report the gate in every output style, and give rejection precedence in the aggregate without creating or updating artifacts.
a062b019The GNATdoc workaround accepts any generic-formal warning from several benchmark units, so a newly undocumented public formal can pass documentation verification unnoticed.
Solution: Match only known GNATdoc false positives
Split the benchmark-unit filters and enumerate the exact root, reporter, sweep, and suite generic formal names that GNATdoc 26 misclassifies while continuing to reject every other warning.
962e5a48Hosted-runner memory faults currently end with only a runtime Storage_Error location. The local action log cannot identify which benchmark or reporter frame raised it, so platform-only failures are not actionable from CI evidence.
Solution: Capture symbolic test exception tracebacks
Build test mains with debug information and GNAT symbolic exception traceback support. On smoke failure, emit the complete exception occurrence to standard error before preserving the original unhandled failure.
a637cc13The reporting-failure scenario closes the caller's Ada.Text_IO file from inside a benchmark callback while Execute still holds and uses that file. GNAT platforms may retain a dangling internal file view, so the subsequent CSV header write can raise erroneous-memory-access instead of the Status_Error the test expects.
Solution: Keep runner streams valid for the execution lifetime
Remove the invalid cross-callback close scenario and document that Output and Progress must remain open and must not be reset or closed until Execute returns. Existing callback-failure tests continue to cover runner failure accounting without invoking dangling Text_IO behavior.
6f2cf34cAdd isolated benchmark worker processes8a195f6dHarden isolated benchmark worker containment2fb07b32Harden benchmark worker ownership validationc537aff4Harden benchmark worker result validationafb23533Protect benchmark worker environment metadataa29271b3Harden benchmark worker metadata validation3d0ce30aFresh-process executables must currently reconstruct case selection beside the suite registry, so the parent identity and worker callback can drift. Readiness and result return are also repeated in each worker main.
Solution: Execute worker requests through the suite registry
Validate the requested identity and result kind against an exact registration, rebuild worker-local configuration, announce readiness, invoke Execute_One, and return the matching protocol result. Update the maintained example to use this shared dispatch path.
c9c465f1Add fresh-process benchmark workers
6b7273a1Flyology and its standalone subcrates lack one enforced line-length and encoding policy. GNAT-preprocessed units also require a documented path that preserves every conditional branch.
Solution: Standardize Ada formatting at 110 columns
Configure each owning project for 110-column UTF-8 GNATformat output and matching compiler checks, align runtime compilation, document the preprocessing workflow, and reformat all tracked handwritten Ada sources.
ccf08b27GNATformat skips Ada units containing preprocessing directives, while the former test-hook switches made production code generation depend on a preprocessing pass. The repository also lacked an isolated regression proving that every hook selection is correct and that disabled calls disappear at all supported optimization levels.
Solution: Select hook packages through GPR source directories
Replace Ada directives with documented enabled and disabled private package variants, guard hook references with literal constants, and expose imported sentinels for missed guards. Add a dedicated cross-platform CI job that checks all 256 selections, optimization modes, and production archives without adding the exhaustive pass to the ordinary test suite.
edf9d8c0GNAT's runtime style checker rejects a formatter-shifted comment in the Linux file engine, preventing runtime assembly. GNATformat also separates three leading GNATdoc @exclude tags from their declarations, causing the Pages build to report those internal adapter instances as undocumented public API.
Solution: Restore validation-sensitive comment placement
Place the runtime comment in the GNAT style column, keep each @exclude tag adjacent to its declaration, and document the GNATformat exception in the agent guide.
2987d103An owner-stack connection protocol can arm hidden transport and lifecycle readiness through a Capability, but it cannot include the opaque Outbound_Wakeup used by concurrent stream publishers. Multiplexed clients therefore lack one bounded wait set and would need a helper pump or exposed descriptor.
Solution: compose outbound publication with transport arming
Add the narrow Arm_Transport overload that arms the required hidden transport direction, outbound wake source, and lifecycle sources on one outer operation. Consume already-pending notifications before rescheduling to prevent stale-signal spin, preserve independent deadlines, and cover pending, later-signal, reuse, TLS, deadline, native, and lightweight paths.
3815cc9dEstablished stream and datagram operations can block on transport readiness, but most scoped stream calls and Receive_Datagram cannot include caller-owned cancellation or protocol-output descriptors. DNS, HTTP/2, and HTTP/3 parents therefore cannot wake hidden socket work without a helper.
Solution: add bounded interrupt sets to scoped socket operations
Add source-compatible Interrupts parameters to scoped stream array operations and datagram receive roots, copy the bounded descriptor set into caller-owned operation storage, and arm it with transport readiness. Retain typed interruption and device failures and cover already-pending and later-signalled datagram interrupts in native and lightweight lanes.
ebf80d6bFlyology.IO.DNS exposes only synchronous Resolve calls, so an HTTP parent cannot preserve one owner-stack deadline and cancellation scope while resolving an origin. Wrapping the synchronous resolver would block a lightweight event-loop pthread and cannot participate in Continue_After or completion gates.
Solution: implement a bounded scoped DNS resolver state machine
Add reusable resolver configuration snapshots, limited Resolve operations with root and established starts, and typed Finish parity. Drive numeric, localhost, cache, search, family, retry, UDP, TCP fallback, token, deadline, and cancellation paths through hidden scoped socket children, with transactional capacity rollback and complete drain-before-publication tests in both lanes.
c6b1bffcAdd composable scoped DNS resolution
c6b1bffc → updated to 417e48adindex f41acb682 source commits
56a47436HTTP multiplexing needs one operation to wait on hidden transport, outbound,
lifecycle, token, and caller-source descriptors. The four-source bound overflows
H2, and datagram receives cannot express a direction-sensitive caller source
for H3.
Solution: Raise readiness capacity and add caller-source arming overloads
Raise the operation, public I/O, and runtime limits to six interests per
operation. Add connection and datagram overloads that include one validated,
caller-borrowed read-or-write descriptor in the existing bounded wait, retain
the outbound consume-before-reschedule rule, and cover native and lightweight
readiness, race, deadline, cancellation, and reuse behavior.
417e48adPreserve the reviewed Problem/Solution prerequisite commit and merge it after the complete Linux/macOS, SPARK, TLA+, and Pages matrix passed.
0.1.1-devBounded in-memory tracing and gauges for Ada
ef6841e7 → updated to 4ec71932index 73e6c1b57 source commits
c9ccf71bFlyology lacks a generation protocol, listener-capability handoff, and coordinated canary lifecycle for replacing a running server with a changed executable. Operators cannot prepare, admit, promote, cancel, drain, fence, or reverse generations through one reviewed ownership model.
Solution: Add supervised process-generation upgrades and executable guide
Implement the Ada-owned coordinator and agent protocol, narrow descriptor and bootstrap handoff mechanisms, lifecycle policy, ABI and behavioral checks, and a mechanically extracted two-artifact website guide that exercises promotion and cancellation.
1fb1025eReaders can follow coordinator calls without knowing what behavior changes between the two artifacts or what the verification request proves.
Solution: Explain the example server and its observable cutover
Describe the one-byte request/reply behavior, the distinct generation replies, and how promotion and cancellation assertions relate to new connections.
5ec82af6Add supervised process-generation upgrades
270984a3The standalone flyology_bench Alire action captures command output in alire/alr_test_local.log. Hosted failures currently retain only Alire’s generic wrapper, so maintainers cannot identify which redirected smoke executable or assertion failed.
Solution: Surface benchmark test diagnostics on failure
Print the tail of the captured Alire log while preserving the original status, and label the two redirected smoke executables so the retained log names the active command. Successful test output remains unchanged apart from the command labels.
0081ba78The flyology_bench smoke fixture accumulates every calibrated operation in a Natural. Fast x86-64 runners can execute more than Natural Last operations across the suite, raising Constraint_Error before the runtime-specific tests begin.
Solution: Use a modular 64-bit operation counter
Store the volatile sink and scheduler-probe value directly as Interfaces.Unsigned_64. The counter now retains the intended observable work signal without a checked 32-bit accumulation limit.
04ff6dcd* Problem: Managed clients require raw socket adoption boilerplate
Composable protocol clients need a managed Flyology connection before they can use the public connection driver, but callers must create and connect a raw socket and then transfer it through Take. This obscures the admission, deadline, cancellation, and cleanup boundary.
Solution: Add a direct managed connection path
Add Connections.Connect to reserve admission, create and connect the endpoint-family socket under one deadline, and atomically adopt it with existing guards. Cover both lanes, failure cleanup, TLS and driver composition, and document raw and managed client choices.
* Problem: The managed connection test iterates a platform type
Execution_Model is discrete on Darwin but an access type on Linux, so iterating its subtype prevents the new smoke test from compiling on a supported platform. The Darwin loop also repeats the configured project default rather than naming the two lanes under test.
Solution: Run the two execution lanes explicitly
Move the scenarios into Run_All and invoke it once for Lightweight_Task and once for Native_Task. This preserves the intended parity coverage without platform-dependent subtype iteration or a redundant default-lane pass.
4ec71932* Problem: Managed connection setup cannot join completion sets
* Problem: Managed connect lacks a selective-wait regression
The scoped construction tests use Wait_All, so they do not directly prove that a managed connect remains visible as one parent result while its hidden child is excluded from a heterogeneous success gate.
Solution: exercise managed connect with a timer success gate
Run the gate in both execution models, transfer the winning connection through typed Finish, cancel the timer sibling, and assert peer-close and admission cleanup.
4ec71932 → updated to c6b1bffcindex 980bb9e547 source commits
12eb5e12Problem: Benchmark confidence policy is fixeda9567c3bProblem: Bootstrap settings permit pathological analysis2a4f4862The statistical-policy changes alter the generated GNATdoc identity for Compare_Independent. The benchmarking guide and comparison journal retain the previous fragment, so the Pages link checker rejects the site built for PR #62.
Solution: Point both links at the regenerated entity anchor
Update the two deep links to the fragment emitted for Compare_Independent by the current flyology_bench API documentation.
98f7201aCurrent main widens the benchmark smoke counter to Unsigned_64 so fast hosts cannot overflow it. The statistical-policy layer snapshots that counter as Natural, making the rebased stack fail compilation before any benchmark runs.
Solution: Match the snapshot to the widened counter
Store the pre-execution snapshot as Interfaces.Unsigned_64 so assignment and comparison remain type-correct across the counter full range.
1c7a45e4The statistical smoke compares two live timing medians against a fixed fourfold ratio. Very fast operations can calibrate into dissimilar observed slices under scheduler and timer noise even though both slices are valid, causing intermittent Program_Error before the benchmark API is exercised further.
Solution: Validate timed-slice validity instead of proximity
Require both retained median batch durations to be positive. The smoke still verifies that equal-time comparison produced timed samples and separately verifies the configured batching policy, without treating host timing noise as a correctness failure.
f628b406The retake and pause checks require a helper process to cross a tick-based host CPU counter window. Runner scheduling can leave no actionable contaminated window, so the test faults even though the harness reports a coherent observation.
Solution: Validate responses only when the host observes them
Use tick-sized interference samples and windows, retain bounded response and sample-count checks, and require pause counters, elapsed time, and retaken samples to agree. A run with no actionable external window is now a valid integration outcome.
713783bfAdd extensible benchmark metrics and manual timingcb1c0c6bFix custom metric comparison reporting1be316cbHarden manual timing adapters340086a7Preserve missing custom probe failurescc7a3938Custom metric summaries and comparisons retain fixed bootstrap sizes and 95 percent interval tails after configurable statistical policy is introduced. Their additional intervals also remain outside the aggregate bootstrap-work ceiling.
Solution: Apply the result's statistical policy to custom metrics
Size custom bootstrap distributions from the configured resample count, derive their interval tails from the configured confidence level, and include every registered custom metric in bootstrap-work validation.
e9100447Add saved baseline regression gates4c4382afFix baseline gate compatibility and reporting8aba46e3Prove baseline regression arithmetic boundsfedeeb77The regression gate fixes every independent comparison at 95 percent confidence and 2,000 resamples even when callers use another benchmark policy. This also leaves the gate's bootstrap work outside the shared safety ceiling.
Solution: Make gate statistics explicit and bounded
Add source-compatible defaulted confidence and resample parameters to gate evaluation, propagate them through comparison and result metadata, validate aggregate bootstrap work, and cover non-default policy retention.
0b9f10beThe saved and current inputs reuse one live measurement, but the independent bootstrap resamples them separately. Scheduler outliers can widen the interval beyond the fixed threshold, causing valid inconclusive results to fail the benchmark test intermittently on CI and locally.
Solution: Exercise gate policy with deterministic samples
Add a test-only child helper that constructs a private measurement from a fixed mildly dispersed vector. Use that measurement for equivalence, boundary, inconclusive, and reporter coverage while retaining live measurements for collection and coarse improvement/regression smoke checks.
de70616eThe Ratio and Time_Change postconditions leave nonlinear floating-point bounds in combined prover searches. GNATprove 16.1.0 can reach its one-second level-one prover limit on Ubuntu CI even though repeated proofs may pass locally.
Solution: Split the floating bounds into stable proof obligations
State Ratio bounds directly in its postcondition and retain the exact Time_Change arithmetic while asserting each difficult bound in its matching branch. The final postcondition becomes a cheap control-flow merge without raising the global proof level or changing returned values.
8b189587Ubuntu GNATprove 16.1.0 still reaches the default one-second limit on the remaining Ratio monotonicity proof and the faster Time_Change bound. Further structural splits relocate the same floating-point search without reducing it.
Solution: Give only the benchmark proof a two-second timeout
Keep level 1 and every other proof invocation unchanged while extending the already-isolated baseline math proof budget. Record the focused budget and its rationale in the benchmark proof status.
7406db05Ubuntu GNATprove 16.1.0 can still exhaust the isolated two-second timeout on the Ratio lower-bound postcondition after the structural proof split. This leaves otherwise valid stack heads intermittently red.
Solution: Widen only the benchmark proof timeout
Increase the Baseline_Math timeout to five seconds while retaining level 1 and every other proof budget. Update the proof-status record to match the executable invocation.
739287caAdd benchmark parameter sweeps and scaling analysisce31f2e2Fix sweep availability and scaling metadata449eff4bFix sweep availability and display reportingdbf06156Fix sweep failure and scaling states03d277e6Fix exact sweep boundaries and reportingac1a8781Test final sweep budget stop semanticscee1d4abAdd explicit flyology_bench suite runner5334042aFix benchmark suite reporting and executione1433649Adding a defaulted context formal lets ordinary calls compile but breaks source uses that depend on the exact legacy reporter profile, including access attributes, renamings, and generic actuals. Ada also forbids overloading generic units.
Solution: Preserve legacy profiles beside contextual reporters
Restore exact pre-suite reporter signatures as forwarding entry points, expose contextual overloads for ordinary procedures and distinctly named contextual multi-way generics, update suite bindings, and compile-check access and renaming compatibility.
b5f078d4The suite aggregate reserves a regression-rejected outcome but registered measurements cannot evaluate a saved baseline. Users must duplicate execution and reporting logic outside the runner, and the suite cannot propagate a gate failure to its final status.
Solution: Add explicit read-only gated registrations
Let ordinary cases opt into a saved-baseline policy, evaluate it with the suite's effective confidence, resample, and seed settings after collection, report the gate in every output style, and give rejection precedence in the aggregate without creating or updating artifacts.
a062b019The GNATdoc workaround accepts any generic-formal warning from several benchmark units, so a newly undocumented public formal can pass documentation verification unnoticed.
Solution: Match only known GNATdoc false positives
Split the benchmark-unit filters and enumerate the exact root, reporter, sweep, and suite generic formal names that GNATdoc 26 misclassifies while continuing to reject every other warning.
962e5a48Hosted-runner memory faults currently end with only a runtime Storage_Error location. The local action log cannot identify which benchmark or reporter frame raised it, so platform-only failures are not actionable from CI evidence.
Solution: Capture symbolic test exception tracebacks
Build test mains with debug information and GNAT symbolic exception traceback support. On smoke failure, emit the complete exception occurrence to standard error before preserving the original unhandled failure.
a637cc13The reporting-failure scenario closes the caller's Ada.Text_IO file from inside a benchmark callback while Execute still holds and uses that file. GNAT platforms may retain a dangling internal file view, so the subsequent CSV header write can raise erroneous-memory-access instead of the Status_Error the test expects.
Solution: Keep runner streams valid for the execution lifetime
Remove the invalid cross-callback close scenario and document that Output and Progress must remain open and must not be reset or closed until Execute returns. Existing callback-failure tests continue to cover runner failure accounting without invoking dangling Text_IO behavior.
6f2cf34cAdd isolated benchmark worker processes8a195f6dHarden isolated benchmark worker containment2fb07b32Harden benchmark worker ownership validationc537aff4Harden benchmark worker result validationafb23533Protect benchmark worker environment metadataa29271b3Harden benchmark worker metadata validation3d0ce30aFresh-process executables must currently reconstruct case selection beside the suite registry, so the parent identity and worker callback can drift. Readiness and result return are also repeated in each worker main.
Solution: Execute worker requests through the suite registry
Validate the requested identity and result kind against an exact registration, rebuild worker-local configuration, announce readiness, invoke Execute_One, and return the matching protocol result. Update the maintained example to use this shared dispatch path.
c9c465f1Add fresh-process benchmark workers
6b7273a1Flyology and its standalone subcrates lack one enforced line-length and encoding policy. GNAT-preprocessed units also require a documented path that preserves every conditional branch.
Solution: Standardize Ada formatting at 110 columns
Configure each owning project for 110-column UTF-8 GNATformat output and matching compiler checks, align runtime compilation, document the preprocessing workflow, and reformat all tracked handwritten Ada sources.
ccf08b27GNATformat skips Ada units containing preprocessing directives, while the former test-hook switches made production code generation depend on a preprocessing pass. The repository also lacked an isolated regression proving that every hook selection is correct and that disabled calls disappear at all supported optimization levels.
Solution: Select hook packages through GPR source directories
Replace Ada directives with documented enabled and disabled private package variants, guard hook references with literal constants, and expose imported sentinels for missed guards. Add a dedicated cross-platform CI job that checks all 256 selections, optimization modes, and production archives without adding the exhaustive pass to the ordinary test suite.
edf9d8c0GNAT's runtime style checker rejects a formatter-shifted comment in the Linux file engine, preventing runtime assembly. GNATformat also separates three leading GNATdoc @exclude tags from their declarations, causing the Pages build to report those internal adapter instances as undocumented public API.
Solution: Restore validation-sensitive comment placement
Place the runtime comment in the GNAT style column, keep each @exclude tag adjacent to its declaration, and document the GNATformat exception in the agent guide.
2987d103An owner-stack connection protocol can arm hidden transport and lifecycle readiness through a Capability, but it cannot include the opaque Outbound_Wakeup used by concurrent stream publishers. Multiplexed clients therefore lack one bounded wait set and would need a helper pump or exposed descriptor.
Solution: compose outbound publication with transport arming
Add the narrow Arm_Transport overload that arms the required hidden transport direction, outbound wake source, and lifecycle sources on one outer operation. Consume already-pending notifications before rescheduling to prevent stale-signal spin, preserve independent deadlines, and cover pending, later-signal, reuse, TLS, deadline, native, and lightweight paths.
3815cc9dEstablished stream and datagram operations can block on transport readiness, but most scoped stream calls and Receive_Datagram cannot include caller-owned cancellation or protocol-output descriptors. DNS, HTTP/2, and HTTP/3 parents therefore cannot wake hidden socket work without a helper.
Solution: add bounded interrupt sets to scoped socket operations
Add source-compatible Interrupts parameters to scoped stream array operations and datagram receive roots, copy the bounded descriptor set into caller-owned operation storage, and arm it with transport readiness. Retain typed interruption and device failures and cover already-pending and later-signalled datagram interrupts in native and lightweight lanes.
ebf80d6bFlyology.IO.DNS exposes only synchronous Resolve calls, so an HTTP parent cannot preserve one owner-stack deadline and cancellation scope while resolving an origin. Wrapping the synchronous resolver would block a lightweight event-loop pthread and cannot participate in Continue_After or completion gates.
Solution: implement a bounded scoped DNS resolver state machine
Add reusable resolver configuration snapshots, limited Resolve operations with root and established starts, and typed Finish parity. Drive numeric, localhost, cache, search, family, retry, UDP, TCP fallback, token, deadline, and cancellation paths through hidden scoped socket children, with transactional capacity rollback and complete drain-before-publication tests in both lanes.
c6b1bffcAdd composable scoped DNS resolution
c6b1bffc → updated to 417e48adindex f41acb682 source commits
56a47436HTTP multiplexing needs one operation to wait on hidden transport, outbound,
lifecycle, token, and caller-source descriptors. The four-source bound overflows
H2, and datagram receives cannot express a direction-sensitive caller source
for H3.
Solution: Raise readiness capacity and add caller-source arming overloads
Raise the operation, public I/O, and runtime limits to six interests per
operation. Add connection and datagram overloads that include one validated,
caller-borrowed read-or-write descriptor in the existing bounded wait, retain
the outbound consume-before-reschedule rule, and cover native and lightweight
readiness, race, deadline, cancellation, and reuse behavior.
417e48adPreserve the reviewed Problem/Solution prerequisite commit and merge it after the complete Linux/macOS, SPARK, TLA+, and Pages matrix passed.
0.1.0-devMemory-node topology reporting for Ada
ef6841e7 → updated to 4ec71932index 73e6c1b57 source commits
c9ccf71bFlyology lacks a generation protocol, listener-capability handoff, and coordinated canary lifecycle for replacing a running server with a changed executable. Operators cannot prepare, admit, promote, cancel, drain, fence, or reverse generations through one reviewed ownership model.
Solution: Add supervised process-generation upgrades and executable guide
Implement the Ada-owned coordinator and agent protocol, narrow descriptor and bootstrap handoff mechanisms, lifecycle policy, ABI and behavioral checks, and a mechanically extracted two-artifact website guide that exercises promotion and cancellation.
1fb1025eReaders can follow coordinator calls without knowing what behavior changes between the two artifacts or what the verification request proves.
Solution: Explain the example server and its observable cutover
Describe the one-byte request/reply behavior, the distinct generation replies, and how promotion and cancellation assertions relate to new connections.
5ec82af6Add supervised process-generation upgrades
270984a3The standalone flyology_bench Alire action captures command output in alire/alr_test_local.log. Hosted failures currently retain only Alire’s generic wrapper, so maintainers cannot identify which redirected smoke executable or assertion failed.
Solution: Surface benchmark test diagnostics on failure
Print the tail of the captured Alire log while preserving the original status, and label the two redirected smoke executables so the retained log names the active command. Successful test output remains unchanged apart from the command labels.
0081ba78The flyology_bench smoke fixture accumulates every calibrated operation in a Natural. Fast x86-64 runners can execute more than Natural Last operations across the suite, raising Constraint_Error before the runtime-specific tests begin.
Solution: Use a modular 64-bit operation counter
Store the volatile sink and scheduler-probe value directly as Interfaces.Unsigned_64. The counter now retains the intended observable work signal without a checked 32-bit accumulation limit.
04ff6dcd* Problem: Managed clients require raw socket adoption boilerplate
Composable protocol clients need a managed Flyology connection before they can use the public connection driver, but callers must create and connect a raw socket and then transfer it through Take. This obscures the admission, deadline, cancellation, and cleanup boundary.
Solution: Add a direct managed connection path
Add Connections.Connect to reserve admission, create and connect the endpoint-family socket under one deadline, and atomically adopt it with existing guards. Cover both lanes, failure cleanup, TLS and driver composition, and document raw and managed client choices.
* Problem: The managed connection test iterates a platform type
Execution_Model is discrete on Darwin but an access type on Linux, so iterating its subtype prevents the new smoke test from compiling on a supported platform. The Darwin loop also repeats the configured project default rather than naming the two lanes under test.
Solution: Run the two execution lanes explicitly
Move the scenarios into Run_All and invoke it once for Lightweight_Task and once for Native_Task. This preserves the intended parity coverage without platform-dependent subtype iteration or a redundant default-lane pass.
4ec71932* Problem: Managed connection setup cannot join completion sets
* Problem: Managed connect lacks a selective-wait regression
The scoped construction tests use Wait_All, so they do not directly prove that a managed connect remains visible as one parent result while its hidden child is excluded from a heterogeneous success gate.
Solution: exercise managed connect with a timer success gate
Run the gate in both execution models, transfer the winning connection through typed Finish, cancel the timer sibling, and assert peer-close and admission cleanup.
4ec71932 → updated to c6b1bffcindex 980bb9e547 source commits
12eb5e12Problem: Benchmark confidence policy is fixeda9567c3bProblem: Bootstrap settings permit pathological analysis2a4f4862The statistical-policy changes alter the generated GNATdoc identity for Compare_Independent. The benchmarking guide and comparison journal retain the previous fragment, so the Pages link checker rejects the site built for PR #62.
Solution: Point both links at the regenerated entity anchor
Update the two deep links to the fragment emitted for Compare_Independent by the current flyology_bench API documentation.
98f7201aCurrent main widens the benchmark smoke counter to Unsigned_64 so fast hosts cannot overflow it. The statistical-policy layer snapshots that counter as Natural, making the rebased stack fail compilation before any benchmark runs.
Solution: Match the snapshot to the widened counter
Store the pre-execution snapshot as Interfaces.Unsigned_64 so assignment and comparison remain type-correct across the counter full range.
1c7a45e4The statistical smoke compares two live timing medians against a fixed fourfold ratio. Very fast operations can calibrate into dissimilar observed slices under scheduler and timer noise even though both slices are valid, causing intermittent Program_Error before the benchmark API is exercised further.
Solution: Validate timed-slice validity instead of proximity
Require both retained median batch durations to be positive. The smoke still verifies that equal-time comparison produced timed samples and separately verifies the configured batching policy, without treating host timing noise as a correctness failure.
f628b406The retake and pause checks require a helper process to cross a tick-based host CPU counter window. Runner scheduling can leave no actionable contaminated window, so the test faults even though the harness reports a coherent observation.
Solution: Validate responses only when the host observes them
Use tick-sized interference samples and windows, retain bounded response and sample-count checks, and require pause counters, elapsed time, and retaken samples to agree. A run with no actionable external window is now a valid integration outcome.
713783bfAdd extensible benchmark metrics and manual timingcb1c0c6bFix custom metric comparison reporting1be316cbHarden manual timing adapters340086a7Preserve missing custom probe failurescc7a3938Custom metric summaries and comparisons retain fixed bootstrap sizes and 95 percent interval tails after configurable statistical policy is introduced. Their additional intervals also remain outside the aggregate bootstrap-work ceiling.
Solution: Apply the result's statistical policy to custom metrics
Size custom bootstrap distributions from the configured resample count, derive their interval tails from the configured confidence level, and include every registered custom metric in bootstrap-work validation.
e9100447Add saved baseline regression gates4c4382afFix baseline gate compatibility and reporting8aba46e3Prove baseline regression arithmetic boundsfedeeb77The regression gate fixes every independent comparison at 95 percent confidence and 2,000 resamples even when callers use another benchmark policy. This also leaves the gate's bootstrap work outside the shared safety ceiling.
Solution: Make gate statistics explicit and bounded
Add source-compatible defaulted confidence and resample parameters to gate evaluation, propagate them through comparison and result metadata, validate aggregate bootstrap work, and cover non-default policy retention.
0b9f10beThe saved and current inputs reuse one live measurement, but the independent bootstrap resamples them separately. Scheduler outliers can widen the interval beyond the fixed threshold, causing valid inconclusive results to fail the benchmark test intermittently on CI and locally.
Solution: Exercise gate policy with deterministic samples
Add a test-only child helper that constructs a private measurement from a fixed mildly dispersed vector. Use that measurement for equivalence, boundary, inconclusive, and reporter coverage while retaining live measurements for collection and coarse improvement/regression smoke checks.
de70616eThe Ratio and Time_Change postconditions leave nonlinear floating-point bounds in combined prover searches. GNATprove 16.1.0 can reach its one-second level-one prover limit on Ubuntu CI even though repeated proofs may pass locally.
Solution: Split the floating bounds into stable proof obligations
State Ratio bounds directly in its postcondition and retain the exact Time_Change arithmetic while asserting each difficult bound in its matching branch. The final postcondition becomes a cheap control-flow merge without raising the global proof level or changing returned values.
8b189587Ubuntu GNATprove 16.1.0 still reaches the default one-second limit on the remaining Ratio monotonicity proof and the faster Time_Change bound. Further structural splits relocate the same floating-point search without reducing it.
Solution: Give only the benchmark proof a two-second timeout
Keep level 1 and every other proof invocation unchanged while extending the already-isolated baseline math proof budget. Record the focused budget and its rationale in the benchmark proof status.
7406db05Ubuntu GNATprove 16.1.0 can still exhaust the isolated two-second timeout on the Ratio lower-bound postcondition after the structural proof split. This leaves otherwise valid stack heads intermittently red.
Solution: Widen only the benchmark proof timeout
Increase the Baseline_Math timeout to five seconds while retaining level 1 and every other proof budget. Update the proof-status record to match the executable invocation.
739287caAdd benchmark parameter sweeps and scaling analysisce31f2e2Fix sweep availability and scaling metadata449eff4bFix sweep availability and display reportingdbf06156Fix sweep failure and scaling states03d277e6Fix exact sweep boundaries and reportingac1a8781Test final sweep budget stop semanticscee1d4abAdd explicit flyology_bench suite runner5334042aFix benchmark suite reporting and executione1433649Adding a defaulted context formal lets ordinary calls compile but breaks source uses that depend on the exact legacy reporter profile, including access attributes, renamings, and generic actuals. Ada also forbids overloading generic units.
Solution: Preserve legacy profiles beside contextual reporters
Restore exact pre-suite reporter signatures as forwarding entry points, expose contextual overloads for ordinary procedures and distinctly named contextual multi-way generics, update suite bindings, and compile-check access and renaming compatibility.
b5f078d4The suite aggregate reserves a regression-rejected outcome but registered measurements cannot evaluate a saved baseline. Users must duplicate execution and reporting logic outside the runner, and the suite cannot propagate a gate failure to its final status.
Solution: Add explicit read-only gated registrations
Let ordinary cases opt into a saved-baseline policy, evaluate it with the suite's effective confidence, resample, and seed settings after collection, report the gate in every output style, and give rejection precedence in the aggregate without creating or updating artifacts.
a062b019The GNATdoc workaround accepts any generic-formal warning from several benchmark units, so a newly undocumented public formal can pass documentation verification unnoticed.
Solution: Match only known GNATdoc false positives
Split the benchmark-unit filters and enumerate the exact root, reporter, sweep, and suite generic formal names that GNATdoc 26 misclassifies while continuing to reject every other warning.
962e5a48Hosted-runner memory faults currently end with only a runtime Storage_Error location. The local action log cannot identify which benchmark or reporter frame raised it, so platform-only failures are not actionable from CI evidence.
Solution: Capture symbolic test exception tracebacks
Build test mains with debug information and GNAT symbolic exception traceback support. On smoke failure, emit the complete exception occurrence to standard error before preserving the original unhandled failure.
a637cc13The reporting-failure scenario closes the caller's Ada.Text_IO file from inside a benchmark callback while Execute still holds and uses that file. GNAT platforms may retain a dangling internal file view, so the subsequent CSV header write can raise erroneous-memory-access instead of the Status_Error the test expects.
Solution: Keep runner streams valid for the execution lifetime
Remove the invalid cross-callback close scenario and document that Output and Progress must remain open and must not be reset or closed until Execute returns. Existing callback-failure tests continue to cover runner failure accounting without invoking dangling Text_IO behavior.
6f2cf34cAdd isolated benchmark worker processes8a195f6dHarden isolated benchmark worker containment2fb07b32Harden benchmark worker ownership validationc537aff4Harden benchmark worker result validationafb23533Protect benchmark worker environment metadataa29271b3Harden benchmark worker metadata validation3d0ce30aFresh-process executables must currently reconstruct case selection beside the suite registry, so the parent identity and worker callback can drift. Readiness and result return are also repeated in each worker main.
Solution: Execute worker requests through the suite registry
Validate the requested identity and result kind against an exact registration, rebuild worker-local configuration, announce readiness, invoke Execute_One, and return the matching protocol result. Update the maintained example to use this shared dispatch path.
c9c465f1Add fresh-process benchmark workers
6b7273a1Flyology and its standalone subcrates lack one enforced line-length and encoding policy. GNAT-preprocessed units also require a documented path that preserves every conditional branch.
Solution: Standardize Ada formatting at 110 columns
Configure each owning project for 110-column UTF-8 GNATformat output and matching compiler checks, align runtime compilation, document the preprocessing workflow, and reformat all tracked handwritten Ada sources.
ccf08b27GNATformat skips Ada units containing preprocessing directives, while the former test-hook switches made production code generation depend on a preprocessing pass. The repository also lacked an isolated regression proving that every hook selection is correct and that disabled calls disappear at all supported optimization levels.
Solution: Select hook packages through GPR source directories
Replace Ada directives with documented enabled and disabled private package variants, guard hook references with literal constants, and expose imported sentinels for missed guards. Add a dedicated cross-platform CI job that checks all 256 selections, optimization modes, and production archives without adding the exhaustive pass to the ordinary test suite.
edf9d8c0GNAT's runtime style checker rejects a formatter-shifted comment in the Linux file engine, preventing runtime assembly. GNATformat also separates three leading GNATdoc @exclude tags from their declarations, causing the Pages build to report those internal adapter instances as undocumented public API.
Solution: Restore validation-sensitive comment placement
Place the runtime comment in the GNAT style column, keep each @exclude tag adjacent to its declaration, and document the GNATformat exception in the agent guide.
2987d103An owner-stack connection protocol can arm hidden transport and lifecycle readiness through a Capability, but it cannot include the opaque Outbound_Wakeup used by concurrent stream publishers. Multiplexed clients therefore lack one bounded wait set and would need a helper pump or exposed descriptor.
Solution: compose outbound publication with transport arming
Add the narrow Arm_Transport overload that arms the required hidden transport direction, outbound wake source, and lifecycle sources on one outer operation. Consume already-pending notifications before rescheduling to prevent stale-signal spin, preserve independent deadlines, and cover pending, later-signal, reuse, TLS, deadline, native, and lightweight paths.
3815cc9dEstablished stream and datagram operations can block on transport readiness, but most scoped stream calls and Receive_Datagram cannot include caller-owned cancellation or protocol-output descriptors. DNS, HTTP/2, and HTTP/3 parents therefore cannot wake hidden socket work without a helper.
Solution: add bounded interrupt sets to scoped socket operations
Add source-compatible Interrupts parameters to scoped stream array operations and datagram receive roots, copy the bounded descriptor set into caller-owned operation storage, and arm it with transport readiness. Retain typed interruption and device failures and cover already-pending and later-signalled datagram interrupts in native and lightweight lanes.
ebf80d6bFlyology.IO.DNS exposes only synchronous Resolve calls, so an HTTP parent cannot preserve one owner-stack deadline and cancellation scope while resolving an origin. Wrapping the synchronous resolver would block a lightweight event-loop pthread and cannot participate in Continue_After or completion gates.
Solution: implement a bounded scoped DNS resolver state machine
Add reusable resolver configuration snapshots, limited Resolve operations with root and established starts, and typed Finish parity. Drive numeric, localhost, cache, search, family, retry, UDP, TCP fallback, token, deadline, and cancellation paths through hidden scoped socket children, with transactional capacity rollback and complete drain-before-publication tests in both lanes.
c6b1bffcAdd composable scoped DNS resolution
c6b1bffc → updated to 417e48adindex f41acb682 source commits
56a47436HTTP multiplexing needs one operation to wait on hidden transport, outbound,
lifecycle, token, and caller-source descriptors. The four-source bound overflows
H2, and datagram receives cannot express a direction-sensitive caller source
for H3.
Solution: Raise readiness capacity and add caller-source arming overloads
Raise the operation, public I/O, and runtime limits to six interests per
operation. Add connection and datagram overloads that include one validated,
caller-borrowed read-or-write descriptor in the existing bounded wait, retain
the outbound consume-before-reschedule rule, and cover native and lightweight
readiness, race, deadline, cancellation, and reuse behavior.
417e48adPreserve the reviewed Problem/Solution prerequisite commit and merge it after the complete Linux/macOS, SPARK, TLA+, and Pages matrix passed.
73e6c1b5 on GitHub
0.1.1-devAllocation-conscious URI, IRI, and URL reference parser
0.1.1-devPostgres client and protocol server for Flyology tasks
0.1.1-devVersioned PostgreSQL raw SQL parser and Ada syntax tree
0.1.1-devShared native runtime for versioned Flyology PostgreSQL parsers
0.1.1-devNative PostgreSQL 14 parser and owned Ada AST
0.1.1-devNative PostgreSQL 15 parser and owned Ada AST
0.1.1-devNative PostgreSQL 16 parser and owned Ada AST
0.1.1-devNative PostgreSQL 17 parser and owned Ada AST
0.1.1-devNative PostgreSQL 18 parser and owned Ada AST
0.1.1-devPortable, strongly typed SIMD foundations for Ada
0.1.1-devExperimental GNAT runtime extension for native and lightweight Ada tasks
c2db3e42 → updated to 159210caindex 23c79db920 source commits
df4d1837Add composable scoped I/O operations9b8f71e8Preserve scoped file timeout semantics69a526a8Queue scoped files under submission pressurea0190f51Make operation rearming transactionaled0d6fa9Add public operation continuations00d106aaExpand scoped file lifecycle coverage7c4b2523Add scoped datagram operations28b228bbAdd scoped Internet connect operations112761c7Add scoped Internet accept operationse0ab1878Add scoped Unix socket operations624d33c5Add owned-buffer file operationscad92a77Add scoped file watcher operations077a9641Add composable task result operations66896e62Add composable bounded channel operationsd01d358dAdd composable buffer channel operations7b1e6b58Complete composable connection and watcher operations3760634bAdd composable connection TLS upgrade1810fc60Add set-independent connection driver capability195b2289Add composable standalone TLS operations159210caAdd composable scoped operations across Flyology I/O
159210ca → updated to ef6841e7index fe947caf1 source commit
ef6841e7Flyology's dependency pre-build runs nested alr printenv commands while the outer Alire action exports deployment-specific Flyology paths. Alire hashes those inherited values and can deploy the same release twice, then reject the conflicting generated prefix.
Solution: Isolate nested toolchain discovery
Scrub only Flyology's root, generated configuration, and generated prefix from both recursive printenv passes. Preserve the public manifest exports and toolchain fast paths, and cover compiler and GPRbuild discovery with a deterministic stale-environment regression.
0.1.0-devCaller-owned memory allocation algorithms for Ada
c2db3e42 → updated to 159210caindex 23c79db920 source commits
df4d1837Add composable scoped I/O operations9b8f71e8Preserve scoped file timeout semantics69a526a8Queue scoped files under submission pressurea0190f51Make operation rearming transactionaled0d6fa9Add public operation continuations00d106aaExpand scoped file lifecycle coverage7c4b2523Add scoped datagram operations28b228bbAdd scoped Internet connect operations112761c7Add scoped Internet accept operationse0ab1878Add scoped Unix socket operations624d33c5Add owned-buffer file operationscad92a77Add scoped file watcher operations077a9641Add composable task result operations66896e62Add composable bounded channel operationsd01d358dAdd composable buffer channel operations7b1e6b58Complete composable connection and watcher operations3760634bAdd composable connection TLS upgrade1810fc60Add set-independent connection driver capability195b2289Add composable standalone TLS operations159210caAdd composable scoped operations across Flyology I/O
159210ca → updated to ef6841e7index fe947caf1 source commit
ef6841e7Flyology's dependency pre-build runs nested alr printenv commands while the outer Alire action exports deployment-specific Flyology paths. Alire hashes those inherited values and can deploy the same release twice, then reject the conflicting generated prefix.
Solution: Isolate nested toolchain discovery
Scrub only Flyology's root, generated configuration, and generated prefix from both recursive printenv passes. Preserve the public manifest exports and toolchain fast paths, and cover compiler and GPRbuild discovery with a deterministic stale-environment regression.
0.1.1-devAdaptive nanosecond-scale benchmarking for Ada
c2db3e42 → updated to 159210caindex 23c79db920 source commits
df4d1837Add composable scoped I/O operations9b8f71e8Preserve scoped file timeout semantics69a526a8Queue scoped files under submission pressurea0190f51Make operation rearming transactionaled0d6fa9Add public operation continuations00d106aaExpand scoped file lifecycle coverage7c4b2523Add scoped datagram operations28b228bbAdd scoped Internet connect operations112761c7Add scoped Internet accept operationse0ab1878Add scoped Unix socket operations624d33c5Add owned-buffer file operationscad92a77Add scoped file watcher operations077a9641Add composable task result operations66896e62Add composable bounded channel operationsd01d358dAdd composable buffer channel operations7b1e6b58Complete composable connection and watcher operations3760634bAdd composable connection TLS upgrade1810fc60Add set-independent connection driver capability195b2289Add composable standalone TLS operations159210caAdd composable scoped operations across Flyology I/O
159210ca → updated to ef6841e7index fe947caf1 source commit
ef6841e7Flyology's dependency pre-build runs nested alr printenv commands while the outer Alire action exports deployment-specific Flyology paths. Alire hashes those inherited values and can deploy the same release twice, then reject the conflicting generated prefix.
Solution: Isolate nested toolchain discovery
Scrub only Flyology's root, generated configuration, and generated prefix from both recursive printenv passes. Preserve the public manifest exports and toolchain fast paths, and cover compiler and GPRbuild discovery with a deterministic stale-environment regression.
0.1.1-devCache-line-aware storage and cache information for Ada
c2db3e42 → updated to 159210caindex 23c79db920 source commits
df4d1837Add composable scoped I/O operations9b8f71e8Preserve scoped file timeout semantics69a526a8Queue scoped files under submission pressurea0190f51Make operation rearming transactionaled0d6fa9Add public operation continuations00d106aaExpand scoped file lifecycle coverage7c4b2523Add scoped datagram operations28b228bbAdd scoped Internet connect operations112761c7Add scoped Internet accept operationse0ab1878Add scoped Unix socket operations624d33c5Add owned-buffer file operationscad92a77Add scoped file watcher operations077a9641Add composable task result operations66896e62Add composable bounded channel operationsd01d358dAdd composable buffer channel operations7b1e6b58Complete composable connection and watcher operations3760634bAdd composable connection TLS upgrade1810fc60Add set-independent connection driver capability195b2289Add composable standalone TLS operations159210caAdd composable scoped operations across Flyology I/O
159210ca → updated to ef6841e7index fe947caf1 source commit
ef6841e7Flyology's dependency pre-build runs nested alr printenv commands while the outer Alire action exports deployment-specific Flyology paths. Alire hashes those inherited values and can deploy the same release twice, then reject the conflicting generated prefix.
Solution: Isolate nested toolchain discovery
Scrub only Flyology's root, generated configuration, and generated prefix from both recursive printenv passes. Preserve the public manifest exports and toolchain fast paths, and cover compiler and GPRbuild discovery with a deterministic stale-environment regression.
0.1.1-devBounded in-memory tracing and gauges for Ada
c2db3e42 → updated to 159210caindex 23c79db920 source commits
df4d1837Add composable scoped I/O operations9b8f71e8Preserve scoped file timeout semantics69a526a8Queue scoped files under submission pressurea0190f51Make operation rearming transactionaled0d6fa9Add public operation continuations00d106aaExpand scoped file lifecycle coverage7c4b2523Add scoped datagram operations28b228bbAdd scoped Internet connect operations112761c7Add scoped Internet accept operationse0ab1878Add scoped Unix socket operations624d33c5Add owned-buffer file operationscad92a77Add scoped file watcher operations077a9641Add composable task result operations66896e62Add composable bounded channel operationsd01d358dAdd composable buffer channel operations7b1e6b58Complete composable connection and watcher operations3760634bAdd composable connection TLS upgrade1810fc60Add set-independent connection driver capability195b2289Add composable standalone TLS operations159210caAdd composable scoped operations across Flyology I/O
159210ca → updated to ef6841e7index fe947caf1 source commit
ef6841e7Flyology's dependency pre-build runs nested alr printenv commands while the outer Alire action exports deployment-specific Flyology paths. Alire hashes those inherited values and can deploy the same release twice, then reject the conflicting generated prefix.
Solution: Isolate nested toolchain discovery
Scrub only Flyology's root, generated configuration, and generated prefix from both recursive printenv passes. Preserve the public manifest exports and toolchain fast paths, and cover compiler and GPRbuild discovery with a deterministic stale-environment regression.
0.1.0-devMemory-node topology reporting for Ada
c2db3e42 → updated to 159210caindex 23c79db920 source commits
df4d1837Add composable scoped I/O operations9b8f71e8Preserve scoped file timeout semantics69a526a8Queue scoped files under submission pressurea0190f51Make operation rearming transactionaled0d6fa9Add public operation continuations00d106aaExpand scoped file lifecycle coverage7c4b2523Add scoped datagram operations28b228bbAdd scoped Internet connect operations112761c7Add scoped Internet accept operationse0ab1878Add scoped Unix socket operations624d33c5Add owned-buffer file operationscad92a77Add scoped file watcher operations077a9641Add composable task result operations66896e62Add composable bounded channel operationsd01d358dAdd composable buffer channel operations7b1e6b58Complete composable connection and watcher operations3760634bAdd composable connection TLS upgrade1810fc60Add set-independent connection driver capability195b2289Add composable standalone TLS operations159210caAdd composable scoped operations across Flyology I/O
159210ca → updated to ef6841e7index fe947caf1 source commit
ef6841e7Flyology's dependency pre-build runs nested alr printenv commands while the outer Alire action exports deployment-specific Flyology paths. Alire hashes those inherited values and can deploy the same release twice, then reject the conflicting generated prefix.
Solution: Isolate nested toolchain discovery
Scrub only Flyology's root, generated configuration, and generated prefix from both recursive printenv passes. Preserve the public manifest exports and toolchain fast paths, and cover compiler and GPRbuild discovery with a deterministic stale-environment regression.
a3c93cee on GitHub
0.1.2HTTP client and server library for Flyology tasks
flyology =0.1.0flyology_cachelines =0.1.0flyology_quic =0.1.140925f35 on GitHub
0.1.1HTTP client and server library for Flyology tasks
flyology =0.1.0flyology_cachelines =0.1.0flyology_quic =0.1.14634a228 on GitHub
0.1.1Ada-native QUIC transport for Flyology tasks
flyology ~0.1.097a66ee7 on GitHub
0.1.1-devExperimental GNAT runtime extension for native and lightweight Ada tasks
0.1.0-devCaller-owned memory allocation algorithms for Ada
0.1.1-devAdaptive nanosecond-scale benchmarking for Ada
0.1.1-devCache-line-aware storage and cache information for Ada
0.1.1-devBounded in-memory tracing and gauges for Ada
0.1.0-devMemory-node topology reporting for Ada
0.1.1-devPortable, strongly typed SIMD foundations for Ada
gnat 16.1.00.1.1-devExperimental GNAT runtime extension for native and lightweight Ada tasks
91d6a0f6 → updated to 9a24bf68index 510cfcf639 source commits
40849230The functional loopback checks use retry intervals too narrow for occasional macOS hosted-runner stalls. An early client exception also waits behind cancellation coordination and loses the operation that failed, leaving only a generic lifecycle assertion.
Solution: tolerate stalls and report DNS smoke stages
Use functional smoke deadlines that accommodate runner scheduling, let coordination observe early client completion, and report the execution model and operation stage for unexpected exceptions. The dedicated DNS resilience smoke retains narrow deadline coverage.
347604c6Session logs are distributed across project checkouts, worktrees, sister
repositories, and nested agent sessions. Their token counters also contain
cached input, repeated cumulative records, inherited history, and duplicated
Claude message entries, making straightforward aggregation inaccurate.
Solution: add a portable incremental AI usage audit
Add a self-contained PEP 723 Python script that discovers the workspace from
its containing repository and scans associated Codex and Claude Code logs,
including worktrees, subagents, and workflows.
Deduplicate cumulative and inherited records, categorize base input, cache
reads and writes, output and reasoning tokens, and estimate API-style costs
using model-specific pricing. Persist JSON cursors and daily aggregates so
subsequent runs process only new log records.
Provide summary, daily, weekly, provider, and model breakdowns with JSON
output, date filtering, configurable paths and timezone, state rebuilding,
and integrity diagnostics.
73925055The landing page, navigation, guide index, and machine-readable documentation center Flyology Runtime while standalone libraries share the project identity without depending on it. Visitors cannot reliably distinguish the runtime core, runtime-dependent protocol libraries, and independently adoptable components, and several component documentation paths are missing or placed under the wrong section.
Solution: Present Flyology as the project and Runtime as its core
Reorganize the landing page around the project umbrella, add focused Runtime and Libraries pages, move Guide, Architecture, and the main API under Runtime, separate standalone documentation from the Runtime guide, link each published component Guide and API, fix the scrollable guide topic rail, and align README, product, design, journal, build assertions, and llms.txt with the same dependency model.
6cb6887fThe redesigned project pages repeat the experimental label in several nearby locations while the curated Libraries directory has no path to the complete package index. Readers see redundant maturity warnings but cannot easily reach version, dependency, availability, and compiler-build records for every published package.
Solution: Clarify maturity guidance and link the complete crate index
Keep experimental status in the primary explanatory context, make constraints and adoption guidance more direct, and link crates.flyology.org from the Libraries introduction, footer, and llms.txt documentation map.
4738d656Only the Libraries page exposes the complete package catalogue in its footer. Readers arriving on Runtime, Guide, Architecture, Journal, or the project overview have no equivalent utility path to package versions, dependencies, and availability records.
Solution: Add the crate index to every shared footer
Place the Crate index utility link first in each hand-written footer, including the journal entry template, while retaining every page-specific navigation link.
5b7c3925Check_Parent_Cancellation_Is_Prompt in tests/task_scope_faults_smoke requires
every one of 16 rounds to propagate parent cancellation within 5 ms.
Propagation crosses two lightweight-task wakes, and a contended shared runner
can deschedule either of them for milliseconds, so the check reports host
scheduling as often as it reports the mechanism. CI run 32100324387 failed
this assertion on its first attempt and passed on a rerun of the identical
commit 71df9ed, whose only change is .github/workflows/pages.yml. Locally,
96 spinning processes on 16 cores plus nice -n 20 reproduce the failure in
2 of 30 runs, and 1280 rounds measured under that load put the median at
78 us with 0.9 per cent of rounds past 5 ms: the mechanism is prompt and the
bound is reporting the host. A bare pragma Assert also names only a source
line, so a real regression is indistinguishable from a stalled round.
Solution: Require most rounds to be prompt, bound the rest, and report them
The check now measures all 16 rounds, requires at least 10 of them within
1 ms, and requires no round past a 0.5 s sanity ceiling. That is a stronger
claim about the mechanism rather than a weaker one. The retired 10 ms polling
monitor spreads its latency across its period and places about 1.6 of 16
rounds inside 1 ms, so it clears the new bar about once in two million runs
against once in sixty-five thousand for the old whole-round 5 ms bound;
reinstating that monitor put 0 of 16 rounds inside 1 ms and failed the new
check on all five idle runs. Under the load that failed the old check in
11 per cent of runs, the new check passed 60 of 60. A failing round set is
printed to standard error so the next failure names the rounds and their
latencies instead of only the assertion line.
857820c4Problem: Cancellation promptness is asserted as a per-round wall-clock bound
5dfa0d25The site check that validates local links and fragment targets runs only in
the Pages workflow, which triggers on a push to main and on manual dispatch.
No pull-request workflow builds the site, and ci.yml has no site job, so a
change that breaks a link passes every check a reviewer sees, merges, and
then fails the Pages build on main. The site stops publishing at the point
where the mistake is hardest to attribute.
The hand-written pages carry 457 links into generated GNATdoc entries across
eleven guide and architecture pages. GNATdoc names each entry by a hash of
the declaration, so changing a public subprogram's parameters gives it a new
anchor and silently strands every hand-written link to it. Editing the
surrounding documentation comment does not, which makes the failure easy to
miss: prose edits are safe, and a signature change quietly is not.
Solution: Build and check the site on pull requests
The Pages workflow now also runs on pull_request. The build job is
unchanged for a push, and on a pull request it builds the site and runs the
same link check without touching Pages: configuring Pages, uploading the
artifact, and the deploy job are all skipped. A pull request therefore needs
no Pages permission, which also keeps the job usable from a fork.
Concurrency now groups per ref. One group for every event serialized
unrelated pull requests behind each other and behind a deploy. Pushes to main
still share one group and are still never cancelled; a pull request cancels
its own superseded runs.
This adds a toolchain and GNATdoc build to each pull request, which is the
cost of resolving generated anchors: the check can only confirm a fragment
exists by generating the page that defines it.
Checks run: the workflow parses, has no tabs, guards exactly the three
Pages-specific steps, and keeps the link check unguarded. check-site.mjs was
confirmed to catch this class of breakage against a page carrying one stale
API fragment, where it reports "missing fragment target" and exits non-zero.
This pull request triggers the new job, so its own run is the end-to-end
check.
252f4fd7A machine with more than one processor package attaches memory to each of
them, and memory attached to the package a thread runs on is reached faster
than memory attached to another. Nothing in this repository reports that
structure. Ada itself does not describe it either: System.Multiprocessors
covers processors and dispatching, and the language has no notion of a
memory node at all.
Without it, a program that wants its work near its memory has nothing to
place against.
Solution: Adopt flyology_numa as a nested standalone crate that reports it
The crate reads the host description and reports memory nodes, the
processors attached to each, firmware-declared distances, per-node memory,
and processor packages. It does not place memory; that is a separate
question this crate does not answer.
Three decisions shape the interface.
Topology queries are total. A host with no memory-node structure reports one
node holding every processor, because such a host genuinely has one memory
domain. Support distinguishes a description that was read from the host from
that single-domain description. Individual facts a host does not answer use
the existing variant-record convention with Value_Or, so a caller names its
own fallback rather than receiving a plausible number.
Online and permitted nodes are reported separately. A container is shown the
whole machine's node list while a control group restricts what it may
allocate on, so the host description alone does not establish permission.
Allowed_Nodes intersects the description with Mems_allowed_list from the
process status file.
Node and processor numbers are the host's own, and sparse. They match sysfs
paths and numactl output, so a reported number is the number to look up
elsewhere. To_CPU performs the Ada processor mapping GNAT applies unchecked,
and declines when the host's numbering has a gap the mapping cannot carry.
A description this crate cannot read is reported as a shortfall rather than
guessed at. Support.Complete goes false when any part of the description was
refused: a node or processor number beyond the represented range, a list
naming more ranges than the reader holds, an unreadable processor list, or a
permitted-node list the host named and the reader did not understand. The
distinction that matters is between a node with no processor attached, which
is an ordinary memory expander, and a node whose processor list could not be
read, which is not known to have none. A description that names nodes but
attaches no processor to any of them is refused outright, because every
machine has a processor.
The sysfs reader names the directories it reads instead of assuming them, so
it is host-independent text processing that compiles and runs everywhere.
This departs from flyology_cachelines, which keeps all Linux code under a
Linux source directory and tests only the host it runs on. The interesting
cases cannot be reached that way: a development machine has one node and a
container on one is single-node too. Twelve recorded descriptions under
tests/fixtures cover two packages of one node each, two packages each
divided into two memory nodes, a node carrying memory with no processor
attached, sparse node numbering, a control-group restriction, a gap in
processor numbering, an older host naming only its ordinary memory, and five
descriptions the reader must refuse or report as incomplete. All are
exercised on macOS, which has none of them.
Set algebra lives in its own unit rather than the root body because
discovery runs during the root package's elaboration and cannot call back
into the body it is elaborating. Each step of the discovery chain names the
body it needs with pragma Elaborate, so the ordering does not rest on the
binder inferring it. Elaborate_All would not serve: its closure reaches back
through the platform spec to the root body itself.
A processor list is bounded at one range per processor rather than at 64,
because a host that enumerates processors round-robin across its nodes gives
each node a list of single-processor ranges.
Verified with ./flyology_numa/scripts/test.sh on macOS aarch64 from a clean
tree: both the host suite and the fixture suite pass, and clean builds of
the linux, macos, and unsupported platform bodies through the crate's
host-selection scenario variable emit no warnings, style diagnostics, or
errors.
376d2481The crate describes which memory nodes a host has and which processors are
attached to each. A program that wants its work near its memory can read
that and then do nothing with it: there is no way to ask that a region's
pages be drawn from a chosen node.
Solution: Add Flyology_NUMA.Placement over the kernel's memory-policy calls
Placement is a separate package from the reporting one because the two fail
differently. Reading a description always succeeds; acting on one often does
not. Support reports which of three states applies before anything is
attempted, and every operation reports its own outcome rather than raising.
The third state is the one worth having. A host may have no placement
interface, or it may have one this process is refused. A container sandbox
commonly refuses these calls while the machine underneath has memory nodes
that other processes are using, and reporting that as a host without the
facility would be wrong. Denied is therefore distinct from Unsupported_Host.
The two are told apart by what the host said about itself rather than by the
error number alone, because a sandbox may answer a refused call by claiming
the call does not exist: a host that described its own memory nodes has the
calls that act on them, so a refusal from such a host is a denial.
Support is settled on the first call that needs it rather than while the
package elaborates. A sandbox that answers a refused call by ending the
process would otherwise end any program that merely links this crate, even
one that never places memory.
Apply_To places a range, Apply_To_Thread places what the calling thread
allocates from then on, Node_Of_Address reports where a page ended up, and
Permitted_Nodes asks the host afresh rather than reusing what it said
earlier. A range must begin on a page boundary because placement acts on
whole pages; an unaligned range is refused before the host is asked.
Three details of the kernel interface are easy to get wrong and silent when
got wrong, so each is handled deliberately.
The node-bit count is decremented by the kernel before the mask is read, so
passing the number of bits meant would drop the highest node. Placement
passes one more than that, which names every node this crate represents
while still asking for only the eight bytes the mask has. Reading the
permitted nodes is bounded differently again: the kernel refuses a count
below the number of nodes the host could ever have, counting nodes that are
merely possible rather than online, so that read uses a buffer covering the
largest node count Linux can be built for.
Two policies take no node set, and the kernel rejects either of them when
one arrives. Passing the set a caller naturally has to hand would therefore
fail, so the set is dropped before the host sees it and the interface's
promise that it is ignored is kept.
The preferred policy takes a single node, not a set. The interface documents
that only the lowest-numbered node is preferred rather than implying the
whole set contributes.
No C library wraps these calls. Their manual pages document a separate
library's wrappers, not the one every program already links, so they are
reached through the generic system-call entry point. That entry point is
variadic, which is one of the reasons this repository admits C, but GNAT's
variadic convention reaches it correctly without a C file and keeps the
crate free of one, as flyology_cachelines is.
Call numbers differ per architecture, and not only in value: the x86-64
table and the table newer architectures share disagree on the order of
get_mempolicy and set_mempolicy. Calling by a number from the wrong table
reaches a working but unrelated kernel service, which is worse than not
calling at all, so each table is written out separately and an architecture
whose numbers are not recorded disables placement rather than borrowing
them.
That failure is silent by nature, so the host suite has an invariant that
exposes it: a host that described its own memory nodes has the calls that
act on them, so reporting no way to place memory means the calls were not
reached. Building for one architecture with another's table was checked to
fail on exactly that assertion rather than passing quietly, and passing a
node set with a policy that takes none was checked to fail before the set
was dropped.
Verified on macOS aarch64, x86-64 Linux, and AArch64 Linux. macOS reports
Unsupported_Host and places nothing. Both Linux architectures report
Supported and complete every operation: every policy applied to a range and
to the calling thread, a written page reported on an online node, an
unaligned range refused, and an empty node set refused for a policy that
needs one. Builds of the macos, linux and unsupported platform bodies across
x86-64, AArch64 and an architecture with no recorded table emit no warnings,
style diagnostics, or errors.
41e2d423Flyology_NUMA.Placement acts on an address range. Using it to put an object
on a chosen node means obtaining page-aligned memory, placing it, carving
objects out of it, and releasing it afterwards, all in the caller's code and
all outside the language's own way of saying where memory comes from.
Ada already has that way: the storage pool of an access type. Nothing here
supplied one.
Solution: Add Flyology_NUMA.Pools, a storage pool whose subpools are nodes
An access type names the pool and each allocation names the node, so where
an object lives is written at the point it is created rather than arranged
around it:
Item : constant Sample_Access := new (On_Node (Arena, Near)) Sample'(...);
Subpools are the part of the language that fits this. A pool alone would fix
the node when the access type was declared; a subpool is chosen per
allocation, which is what naming a node needs to be.
The pool obtains whole pages from the host rather than taking memory from
the ordinary heap. Heap memory may begin partway into a page whose remainder
belongs to something else, and placing that page would move memory this
crate does not own. Pages are placed before any of them is handed out, since
placing them afterwards would leave whatever had already been written where
the host first put it.
Memory is handed out by advancing through pages already obtained, and goes
back to the host when a subpool is deallocated or the pool is finalized.
Freeing a single object does nothing. That suits memory built up and
discarded together, which is what a node-bound arena usually is, and the
interface says so rather than implying a general allocator.
Ownership of the pages is left to the runtime's own teardown rather than
taken over. The runtime keeps the records it needs to finalize an object
inside the very pages that object lives in, and a pool that gave its pages
back first would take those records with them; a pool holding anything with
finalization would then fail on the way out of its own scope. Deallocating
one subpool likewise forgets it, because the runtime has already detached it
by then and a pool that still believed it owned it could never serve that
node again.
Only the policies that name a node can be given to a pool. The two that name
none place nothing on the node the subpool stands for, so a pool built on
one would report memory placed there that never was.
Placement_Reached separates a host that placed the memory from one that
could not, and says which of the two it means: the host accepted the
request. Under a bound policy that puts the pages on the node; under a
preferred one the host stays free to draw from elsewhere. Allocation
succeeds either way, on the reasoning that memory in the wrong place still
runs while no memory does not.
A pool is not task safe, and the interface says so. Two tasks allocating
from one subpool at once can be handed the same address.
Verified on macOS aarch64, x86-64 Linux, and AArch64 Linux, and inside
guests built to have several memory nodes, which no machine to hand does.
scripts/multinode-check.sh boots a two-node guest and a four-node guest
whose distances are uneven and one of whose nodes carries memory with no
processor attached, then runs the host suite inside them. It was run on
both x86-64 and AArch64, each emulating its own architecture, so that the
placement calls a guest makes are the ones its architecture's table names. Those runs are
what exercise choosing between nodes at all: memory bound to each node in
turn and confirmed held by that node, including the node with no processor;
interleaving spread over every node rather than drawn from one; and a node
shown further from another than from itself.
The suite gained cases for the two failures described above, and each was
confirmed to fail before the change and pass after. Builds of the macos,
linux and unsupported platform bodies across x86-64, AArch64 and an
architecture with no recorded placement table emit no warnings, style
diagnostics, or errors.
a416d8dc./scripts/test.sh runs each crate's own entry point by path, so the file
has to carry the executable bit. flyology_numa/scripts/test.sh was recorded
without it and the whole suite stopped there, reporting only that a command
could not be run.
Running the script as an argument to a shell, which is what a person does
while writing one, works either way and hides this.
Solution: Record the executable bit, as the other crates' entry points do
550bc612The crate ships, and nothing outside its own directory says so. It has no
guide page, no generated API reference, and no entry in the library ledger
or the machine-readable index. A reader who finds the other standalone
crates on the site does not find this one.
Solution: Adopt the crate into the documentation and the site
The crate now builds its own GNATdoc reference, and the root documentation
run builds and asserts it alongside the other crates. Publishing it required
documenting the pool primitives the runtime calls, which had no comments,
and describing the pool's discriminants in prose: @field names a record
component, and a discriminant of a private type is neither.
The package renaming that shortened System.Storage_Pools.Subpools in the
public part is now in the private part. GNATdoc reported the public renaming
on every documentation run, and the full name appears twice in the visible
declarations rather than in every generated page.
The guide page follows the order a reader needs: add the crate, read the
node structure, use the permitted nodes rather than the online ones, handle
a host with one memory domain, place memory, allocate from a node, and read
distances and packages for what they say rather than what they suggest.
Every API link on that page was resolved through the generated search index
rather than by reading the generated markup, which does not put an entity's
name where a reader of the HTML would expect it.
The library ledger keeps its alphabetical order, so the new entry is S05 and
the two entries after it move up.
Verified with ./scripts/docs.sh, which builds the reference with no
undocumented-entity warnings, and ./scripts/build-site.sh followed by the
site checker, which reports 144 HTML files and 201 total files. The crate's
own suite still passes.
ae1e3ddfThe project map in the hero names each standalone library, and the
catalogue below it links each one. Both lists stop at the crates that
existed before this one. A reader who arrives at the site and looks for what
Flyology offers does not learn that the crate exists.
Solution: Add the crate to the hero project map and the catalogue
Both lists keep their alphabetical order, so the entry sits between IRI and
SIMD. The catalogue entry links to the guide, as the entries for the other
crates with a guide on this site do.
Verified with ./scripts/build-site.sh and the site checker, and by fetching
the built pages: the hero and catalogue both list the crate, and all 39
internal links on its guide page resolve.
aa8b12edThe page opens with a short definition in a callout and then goes straight
to adding the crate. A reader who does not already know what a memory node
is learns the interface before the reason for it, and a reader who does know
still gets no picture of the machine the crate describes.
Solution: Open with a section that explains memory nodes and draws one
The section states what changes when a machine has more than one processor
package, draws a two-node machine with a local and a remote access, and says
plainly when placement is worth doing at all: a program that fits in cache,
or one that allocates little, does not gain from it. The callout it replaces
said less.
The diagram uses only the classes the figure context defines. An earlier
draft used a class that context does not style, and SVG then fell back to a
solid black fill. That reads as deliberate against a dark background and
wrong against a light one, which is how it survived a first look.
Both themes were checked by computed style rather than by eye. The
containers take the page's own surface and border colours, so they follow
the theme instead of fighting it.
Verified with ./scripts/build-site.sh and the site checker.
7b70cbf6Buddy, best-fit, and TLSF live inside the main package hierarchy, so bare-board runtimes cannot reuse them without also accepting Flyology identity, mapping, and shared-memory concerns.
Solution: Extract allocation algorithms into a standalone crate
Add flyology_allocators 0.1.0-dev with caller-owned regions, timed contention retries, native and ARM bare-board verification, and optimized memory primitives. Keep magic and schema policy in a thin Flyology adapter, pin the new crate from Flyology, and update integration tests and documentation.
7bf86246The extracted algorithms lack a reproducible benchmark against the platform allocator, making hot-path regressions and algorithm tradeoffs difficult to measure.
Solution: Add a dedicated flyology_bench allocator shootout
Add an optional hosted benchmark subcrate with fixed-size and fragmented-churn workloads comparing malloc, Buddy, Best-Fit, and TLSF in balanced paired rounds. Keep benchmark dependencies outside the bare-board crate closure and document release and smoke workflows.
be44ac54Profiling shows recursive Buddy allocation and release spend most of their time rebuilding and validating structure-relative node addresses. Release also validates each handle twice before its first mutation, making small allocation cycles substantially slower than the other standalone algorithms.
Solution: Cache the validated Buddy node-table binding
Retain the process-local node-table base in the attached view after validating its complete native range, use bounded node-relative addressing on hot paths, clear the binding on detach, and remove the redundant release validation while preserving validation before mutation.
283d5e71Profiles of fixed allocation cycles show TLSF spending most of its time in free-list insertion and removal, block initialization, and handle validation. Each logical operation repeatedly recomputes and validates the same block base before touching adjacent scalar fields, leaving the constant-time algorithm dominated by address machinery.
Solution: Reuse validated block bases across TLSF hot paths
Validate each referenced block extent once, carry its process-local base through list mutation, splitting, coalescing, and handle release, and keep stored link, size, generation, state, and bitmap checks intact. Avoid a redundant ready-state check while the arena guard is held and replace size-class division by its exact power-of-two shift.
e9199d66Profiling shows fixed-layout address validation, duplicate split and coalesce writes, and repeated AVL balance reads dominate Best-Fit allocation and release. This leaves both fixed cycles and fragmented churn substantially slower than necessary.
Solution: Streamline checked Best-Fit metadata operations
Cache the last complete block-header location in the local view, use a fixed-field address path that retains attachment and complete-header bounds checks, avoid tree metadata writes that insertion immediately replaces, and compute AVL balance inputs once per ancestor. The stored layout, persisted validation, guard behavior, handle generations, and bare-board dependency boundary remain unchanged.
05d2eb83The benchmark project selects release switches only for its own sources. Imported flyology_allocators units are compiled without optimization, making comparisons against the optimized system malloc misleading and overstating allocator hot-path costs.
Solution: Force a clean optimized benchmark closure
Build the benchmark and all imported Ada sources through gprbuild with explicit O3, debug symbols, Ada 2022, warnings, and validity checks. Document why the runner must apply switches across project boundaries.
77e1d163GNAT reports parenthesized array aggregates as obsolescent when the benchmark closure is compiled as Ada 2022, obscuring useful build diagnostics.
Solution: Adopt Ada 2022 bracketed array aggregates
Convert Flyology Bench array initializers and expressions to bracket syntax while retaining parentheses for record aggregates. Keep the obsolescent warning category enabled so future occurrences remain visible.
77ce4c04Release repeatedly discovers physical neighbors and removes and reinserts free-list entries even when the next allocation can reuse the same size class. This makes steady fixed-size and churn workloads pay fragmentation-recovery costs on every operation.
Solution: Defer TLSF coalescing until allocation requires it
Return released blocks directly to their size class and perform a bounded, checked physical coalescing pass only after a normal TLSF search misses or before destroying an empty arena. Preserve boundary tags and bitmap/list validation, and cover adjacent runs, allocated gaps, attachment, stale handles, and complete teardown with adversarial tests.
562bdd61Lazy coalescing makes worst-case allocation linear and no longer joins neighbors during release, but both capability records and package comments retain the eager algorithm contract. Flyology also reuses the eager persisted identity even though version 2 readers reject valid adjacent free blocks produced by the new implementation.
Solution: Publish the lazy TLSF contract and identity
Report the linear worst-case search and deferred release coalescing in standalone and Flyology packages, document the miss-triggered pass, and advance Flyology TLSF to version/schema 3 so cross-version attachment fails at the immutable header. Add standalone capability coverage and a Flyology regression that rejects the complete eager identity.
288b03a2Profiling shows fixed-size allocation cycles spend most of their Best-Fit time splitting a large free block during allocation and immediately removing and coalescing its remainder during release. Fragmented churn repeats related AVL maintenance even when the same physical block can be reused directly.
Solution: Defer Best-Fit coalescing until allocation needs it
Index released blocks directly for exact-size reuse. Before reporting exhaustion, validate the complete physical chain, coalesce every adjacent free run, repair boundary links, and retry the AVL search. Accept the resulting persisted representation on attachment, canonicalize empty arenas during destruction, report the resulting linear worst-case search capability, and cover adjacent frees, alternating gaps, stale handles, attachment validation, and teardown.
41578832Deferred coalescing leaves adjacent free blocks as a valid persisted Best-Fit state. The earlier eager reader rejects that state, but Flyology still advertises the same outer version and schema, so compatibility fails only after entering nested allocator validation.
Solution: Version the Flyology Best-Fit lazy invariant
Advance the Flyology-only adapter version and schema while retaining its magic. This makes older readers reject lazy images at the outer identity boundary and keeps standalone allocator metadata free of application identity.
ec813587The lazy Best-Fit adapter advances its persisted version and schema, but the Flyology data-structure smoke test neither proves rejection of the eager version-2 identity nor reflects the linear fallback and deferred-coalescing capabilities.
Solution: Exercise the Best-Fit version-3 boundary
Temporarily replace a live Best-Fit arena outer identity with the version-2 values and require attachment to fail closed, then restore the image. Assert the truthful lazy-coalescing capabilities for both Best-Fit and TLSF.
e1315591Small allocation cycles repeatedly collapse a released leaf to the root and split the same path on the next allocation. Profiling shows those release stores and recursive rebuilding dominate Buddy's fixed-size cost.
Solution: Coalesce Buddy trees only when allocation needs it
Keep released nodes in the retained split tree, compact free subtrees before a search reports genuine exhaustion, and compact before empty-arena destruction. Add adversarial coverage for half-tree and root reconstruction plus stale handles, and expose the lazy-coalescing capability accurately.
4ad89a24After deferring coalescing, profiling shows fixed-size Buddy allocation is dominated by walking the same split path. Fragmented churn also revisits released orders even though the releasing view already knows a suitable free node.
Solution: Cache one validated free node per Buddy order
Keep process-local node hints in each attached view, validate persisted state before every hinted reuse, and discard all hints during coalescing and detach. Preserve the persisted layout and fall back to the complete tree for stale or missing hints; cover stale hints across independently attached views.
22998f64Buddy still advertises logarithmic eager-coalescing behavior, an eager Flyology reader can attach a retained all-free split tree and falsely report exhaustion for a parent-sized request, and Destroy may release its guard after a partially failed coalescing pass without poisoning the image.
Solution: Harden lazy Buddy capabilities and lifecycle handling
Advertise linear miss search and deferred coalescing, advance only Flyology's outer Buddy identity to version 3, and verify the retained two-leaf compatibility case. Check that an arena is empty before Destroy mutates it and poison any coalescing failure. Compile the standalone project as Ada 2022 for bracket aggregates and extend standalone and Flyology compatibility tests.
66888189Buddy, Best-Fit, and TLSF now defer coalescing until an allocation miss, but the root, standalone, and website guides still advertise their former fast-path and worst-case behavior. The stale prose also describes a process-shared guard even though the standalone algorithms are usable with arbitrary caller-owned storage.
Solution: Document lazy coalescing and view-scoped state
Describe each successful lookup and miss-triggered coalescing path, report the resulting linear worst-case bound, and use attached-view terminology. Record that stored geometry remains stable while the valid state contract changes and Flyology adapters use version-3 identities.
94cd5b57Buddy, Best-Fit, and TLSF now retain free structure on release and coalesce on allocation misses. Behavioral tests cover examples, but no bounded state-machine review checks shared guard ownership, block partitions, index publication, stale view hints, generation-stamped release, false exhaustion, or operation completion across interleavings.
Solution: Model guarded allocator transitions in TLA+
Add a parameterized extraction for the three algorithms with policy-specific splitting and selection, TLSF bin and bitmap state, Buddy view-local hints, lazy coalescing, retry, generations, and fairness. Add safe TLC configurations plus required false-exhaustion, stale-hint, stale-bitmap, stale-release, and nontermination counterexamples, wire them into the authoritative runner, and document the production mapping and abstraction limits.
4dc3e5c0The bounded Buddy, Best-Fit, and TLSF models establish safety and liveness for their extraction, but review alone does not detect when persisted metadata, view-local hints, generations, handles, or size-class computations diverge from the standalone kernels.
Solution: Compare canonical Ada and TLC allocator traces
Add test-only private-state observers and bounded public-operation traces for all three kernels. Model the same traces in TLA+, compare every operation-boundary snapshot byte-for-byte in the authoritative runner, require no-retry liveness counterexamples, and run the conformance boundary in CI while retaining standalone ARM cross-build isolation.
54d77f0fSolution: add a standalone bitmap-slab and contiguous-span allocator, bridge it into Flyology, benchmark it against the existing policies and malloc, and cover its bounded state machine with positive and deliberately broken TLA+ configurations.
239aa004Solution: publish the affected 32-bit bitmap word with release ordering and acquire it before validating the slot generation. Add bounded safe and deliberately broken TLA+ visibility models so TLC reproduces the former stale-handle acceptance.
195b83bfSolution: add a corruption test that makes one large-span head claim its neighboring live allocation, then validate every head and tail descriptor field before reporting capacity or allowing release to mutate the claimed extent.
7a7bc16fThe hero project map, the standalone catalogue, the library ledger, and
llms.txt each name every standalone crate. This branch adds a new one, and
all four lists stop at the crates that existed when the branch was written.
A reader who arrives at the site and looks for what Flyology offers does not
learn that the crate exists.
Solution: Add the crate to the hero map, catalogue, ledger, and llms.txt
Every list keeps its alphabetical order, so the entry sits first among the
standalone crates and the ledger indices shift from S01..S07 to S02..S08.
The crate publishes neither a guide page on this site nor a generated API,
because scripts/docs.sh does not build one and flyology.gpr excludes the
crate from documentation. The entries therefore link to the source tree
only, as the Flyology QUIC entry already does for the same reason. Adding an
api/flyology_allocators/ link would fail the site checker.
Verified with ./scripts/build-site.sh and the site checker, which passes on
141 HTML files, and by grepping the built pages: the hero, catalogue,
ledger, and llms.txt all name the crate.
7dc419f2This branch turns Allocation_Algorithms.Buddy, Best_Fit, and TLSF from
documented packages into generic instantiations of Allocation_Algorithms
.Contract. GNATdoc emits no unit page and no declaration anchor for an
instantiation, so the three API links the guide carries for them now resolve
to nothing. main gained a site link checker after this branch was written,
so the Pages workflow reports all three and fails.
Solution: Present the three instantiations as code rather than links
AGENTS.md asks that the first explanatory mention of a public entity link to
its verified generated GNATdoc unit or declaration entry. No entry is
generated for these three, so there is nothing to link, and pointing them at
the kernels or at Contract would name a different entity than the prose
does. The surrounding Arenas, Contract, Allocation_Capabilities, and View
links are unaffected and still resolve.
Verified with ./scripts/build-site.sh and the site checker, which now passes
on 141 HTML files and 198 total files with zero problems.
9a24bf68This branch adds Allocation_Algorithms.Slab_Span as a fourth arena allocator
and says so in the README, but the guide section that tells a reader how to
choose one still presents three. It names three adapters, describes three
policies, gives three decision rows, and twice states that all three share
one metadata guard. A reader choosing an allocator from this page does not
learn the fourth exists, and the two counts now contradict the README.
Solution: Describe slab/span alongside the other three and correct the counts
The introductory paragraph names the adapter and states its shape: bitmap
slots in fixed runs for small requests, contiguous runs for larger ones, with
descriptors and generation tables outside the managed bytes. A decision row
gives the selection rule and the reclamation behavior a caller can observe:
requests up to half the configured run size take a generation-stamped slot,
larger requests reserve whole runs, release keeps an emptied slab indexed for
reuse, and a miss reclaims empty slabs to obtain the run or span it needs.
The row also states that the run size is an allocator quantum rather than an
operating-system page, because the name invites the opposite assumption.
The half-run threshold matches the kernel, which sends a request to a slab
class when Requested <= Run_Size / 2 and to a span otherwise. Both "all
three" counts become four. The adapter is a generic instantiation, so like
Buddy, Best_Fit, and TLSF it has no generated GNATdoc entry to link.
Verified with ./scripts/build-site.sh and the site checker, which passes on
141 HTML files and 198 total files, and by parsing the built page: tags
balance and the new row renders between TLSF and the compile-time choice.
9a24bf68 → updated to 60de2c69index 8a8e9de51 source commit
60de2c69flyology.gpr imports flyology_allocators.gpr, and the manifest both declares
flyology_allocators as a dependency and pins it to the in-repo directory.
Alire honours pins only in a root crate's manifest, so a consumer resolving
flyology from the index never receives that dependency. Its solution omits
flyology_allocators entirely and gprbuild then fails with "imported project
file flyology_allocators.gpr not found". A crate whose only dependency is
flyology = "~0.1.1-dev" reproduces it in two lines.
Nothing in this repository reports the fault, because the pin satisfies the
dependency for every build performed here. The pin also hid two further
assumptions that only the in-repo directory could satisfy.
Solution: Resolve the allocators dependency from the index
Drop the pin so the declared dependency resolves the same way for this crate
and for its consumers. The published flyology_allocators 0.1.0-dev is built
from this repository at the same commit and the same subdirectory, so the
source reached is identical; only the path by which it is reached changes.
Configure the organization index in this repository's own workflows. They
carried the community index alone, because the pin meant no job here ever
resolved flyology_allocators from an index, and it is published only in the
organization index. Without that step alr reports an incomplete solution
naming flyology_allocators as missing.
Name the excluded documentation project through the imported project rather
than through a repository path. Documentation.Excluded_Project_Files listed
flyology_allocators/flyology_allocators.gpr, which resolved only while the
crate sat in this working tree; as a dependency its project lives in the
Alire build tree under a content hash. Flyology_Allocators'Project_Dir asks
the imported project where it is, so the exclusion holds wherever the
dependency is deployed and the allocator packages stay out of this crate's
generated API, where they would otherwise be documented twice.
Record the rule that produced the fault in the agent guide: no committed
pins in a crate manifest, alr pin in the consuming workspace for unpublished
work, and a declared dependency must resolve from the index. Name the
gprbuild error there too, since that is where the fault appears and it names
a project file rather than a crate.
60de2c69 → updated to 60de2c69index 28a2302d60de2c69 → updated to 9782c81bindex 75a836be16 source commits
aeb10612The hero project map, the standalone catalogue, the library ledger, and
llms.txt each name every component. Flyology RDF publishes a website, a
guide, and three generated API references at rdf.flyology.org, but the name
appears nowhere in this repository. A reader who arrives at the site and
looks for what Flyology offers does not learn that the project exists.
Solution: Add the project to the hero map, catalogue, ledger, and llms.txt
flyology_rdf depends on flyology_iri only. It needs no runtime preparation
and starts no tasks, so it belongs with the standalone libraries. Every list
keeps its alphabetical order, so the entry sits between Flyology NUMA and
Flyology SIMD and the ledger indices shift from S07..S08 to S08..S09.
The ledger and llms.txt entries carry all three generated references, as the
Flyology Postgres entries already do, because the project site treats the
Notation3 and SPARQL APIs as first-class navigation.
The llms.txt project-structure summary also omitted Flyology Allocators and
Flyology NUMA, which the same file already listed further down. Both names
join the summary here.
Verified with the website-kit site checker, whose output is unchanged from
the pre-change baseline, and by rendering the pages: the hero, catalogue,
and ledger all name the project. The six linked URLs return 200.
2188ee0dThe figure caption claimed "One project, two adoption paths" above two cards
that already carry the same fact in their labels, so the caption restated
what the diagram shows. The two card subtitles were not parallel either:
"Core component" named a role while "No runtime dependency" named a
dependency, so neither pair member helped a reader compare the paths.
The component names below them were plain list items. A reader who found the
name that matched the problem had no way to reach the component from the
diagram and had to scroll to the catalogue to find the same name again.
Solution: Drop the caption, make the subtitles parallel, and link the names
The subtitles now read "Prepare the runtime" and "Add a crate", which state
the two adoption paths as the actions they are. The figure keeps an
accessible name through aria-label, and the figcaption rule leaves the
stylesheet with the element it styled.
Each component name becomes a link to its best available destination, which
is the destination the catalogue below already uses: the project site where
one exists, then the guide, then the generated API, then the source tree.
The pill moves from the list item to the anchor so the whole badge is the
hit target. Links open in a new tab and carry no underline, as requested, so
hover brightens the border and adds a faint fill instead. Each list states
the new-tab behavior once in its aria-label rather than repeating it in
twelve hidden spans.
Verified by rendering the page: every badge computes text-decoration-line
none and cursor pointer, and the hovered badge is distinguishable from its
neighbors. The site checker reports one new line against the baseline,
api/flyology_debug/ from the new Debug badge, which resolves once
scripts/docs.sh generates the GNATdoc output that this partial build omits.
5dfd5d36Two measurement runs that share a machine produce two wrong answers, and
neither run can tell that it happened. The problem is not confined to
benchmarks: a load generator, a soak test, or a profiler makes the machine
loud, and a benchmark needs it quiet. Both want the same thing from opposite
directions, and nothing lets them find each other.
Worse, two runs that each suspend collection when they detect interference
are symmetric controllers observing each other. Each pauses because the other
is loading the machine, both then observe quiet, both resume together, and
both immediately pause again.
Solution: Specify a host CPU lock convention and implement it
The convention is named for the resource rather than for benchmarking, so
that the tools which create load take the same claim as the tools which need
its absence. Conflict detection is flock and nothing else: liveness is the
lock itself, so a holder that dies releases with no cleanup path, no stale
registry entries, and no pid-reuse hazard.
A machine-wide claim takes LOCK_EX on the claim file; a claim on individual
CPUs takes LOCK_SH there and LOCK_EX on one file per CPU. Disjoint core-scoped
claims therefore proceed concurrently while a machine-wide claim excludes them
all, with no registry and no arbiter. Per-CPU claims are acquired in ascending
order, because a total order is what stops two callers with overlapping sets
from deadlocking on each other's partial claims.
The path is a single deterministic default rather than a writability-probing
fallback chain. A chain reintroduces the failure it was meant to prevent:
/run/lock is 1777 on Debian and root-owned 0755 on Red Hat derivatives, so a
root process and an unprivileged process on one machine would probe
differently, land on different files, and each conclude it holds the machine.
$TMPDIR is avoided for the same reason, being per-user on macOS.
File content is diagnostic and is never consulted to decide conflicts. It is
still specified and parseable, one key=value pair per line, because a reader
must be able to name the holder; cwd is included because tool and pid alone do
not distinguish one checkout or CI workspace from another. Readers open the
file without locking, since a shared lock would block behind the very holder
they are trying to identify, and must tolerate content that is empty, stale,
or half-written.
A claim is not proof of exclusivity, and the implementation does not present
it as one. systemd PrivateTmp= bind-mounts a per-service directory over /tmp,
so two services share every CPU and cannot see each other's claim files; that
case is detected from /proc/self/mountinfo and reported as namespace-scoped.
Separate containers are not detectable from inside and need an explicit shared
path. A path can be proven private but never proven machine-wide.
Only the C macros live in the native unit. GNAT's C_Variadic_2 convention
calls open() correctly even on Darwin/AArch64, where variadic arguments are
passed differently from fixed ones, so the protocol, the file grammar, and the
/proc reader are all Ada. The convention compiles on every GNAT this crate
supports, checked against 13.2.2 through 16.1.0 against an alire.toml floor of
13, so no compatibility shim is carried.
Verified by building the crate, and by taking claims on disk: the file matches
the published grammar, parses with awk -F=, is created 0666, and per-CPU files
are named beside the machine file.
13b71a5bCPU_Quiescence samples host counters once, before clock characterization, and
then never looks again. A machine that goes busy halfway through collection
produces a result that reads as clean, and nothing in the measurement, the
console card, or the machine-readable output says otherwise. The gate's own
documentation admits it "cannot prevent new load from arriving after
collection begins", and until now that was the end of the story.
The obvious repair is not available either. Host utilization cannot simply be
compared against a limit during collection the way the preflight does it,
because by then the benchmark thread saturates a core and every sample would
trip the limit.
Solution: Watch for foreign load during collection and say what was found
Foreign load is host busy time minus this process's own CPU time, so load the
benchmark itself creates is correctly not foreign. The estimate scales the
host busy ratio by wall time rather than converting ticks, so the platform
tick length never enters the arithmetic. Cross-checked against top on a loaded
machine, the two agree to within 0.1 percent.
Three responses form a ladder. Observe records and keeps everything. Retake
discards the contaminated window and collects it again. Pause waits for the
host to settle, re-warms, and then collects it again. The re-warm is not
politeness: a resumed run has cold caches, predictors, and frequency state, so
without it the first sample after a pause is the outlier the pause existed to
avoid.
Nothing adjusts a reported statistic. A skew factor derived from observed load
would be invented data, because the cost of interference depends on whether
the contention is an SMT sibling, a shared cache, memory bandwidth, or another
socket, and the same foreign share can mean no slowdown or a large one. Runs
are annotated or repaired, never corrected.
Observation happens between timed samples over windows of whole collection
units, where a unit is one sample, one comparison pair, or one balanced
multi-way round. A response therefore never splits the two halves of a pair
and never lands mid-round, where it would spread interference unevenly across
the cases a round exists to compare fairly. Windows carry a margin over the
configured minimum: sized to only just reach it, ordinary batch jitter drops
them below it and they silently degrade to observation. Host counters are
tick-based, so a window that still falls short is recorded but never acted on.
Discarding a window rolls back what the window changed. Per-sample values are
written by index and survive a retake intact, but the position tally of a
comparison pair and the process-telemetry summaries are running sums, so a
discarded pass gives them back. Without that, a run with eighteen retained
samples and twenty-four retakes would report elapsed time and an average CPU
share covering forty-two samples, most of which were thrown away.
Budgets degrade rather than abort. An exhausted retake or pause budget
continues under Observe and reports it, and paused time is excluded from
Maximum_Sampling_Time because waiting for the host is not collection.
Placement is optional and sharpens attribution: with a strict binding every
other CPU's busy time is foreign outright, and the placed CPU's share is its
busy time minus the placed thread's own. SMT siblings join the watched set,
because a process saturating the sibling of the placed CPU slows the
measurement while leaving the placed CPU's own share clean, which makes
sibling-blind core-scoped observation worse than host-wide observation rather
than better. Placement is Linux-only in practice; Apple Silicon implements no
thread affinity and rejects every request, so the Host_Control documentation
no longer describes Darwin placement as merely advisory.
Placement binds only the calling thread, so another thread of this process can
occupy a watched CPU, where its time is indistinguishable from foreign load.
Each window therefore reads process CPU as well as thread CPU, and their
difference bounds how much of the watched capacity those threads could account
for. Once that bound exceeds the configured foreign limit, the core-scoped
answer can no longer address the question the limit asks: the run drops to
host-wide observation, which is well defined for a multi-threaded process, and
reports Attribution_Diluted. Otherwise a scheduler benchmark run under
placement would spend its retake budget discarding samples over load it
created itself. The window that reveals the dilution is discarded rather than
recorded, because its value was computed under the attribution being
abandoned.
The direction of that fallback is deliberate. Core-scoped attribution
subtracts only the placed thread, which over-reports interference when other
threads share the watched CPUs; subtracting the whole process instead would
deduct time our threads spent on CPUs outside the watched set, under-report
foreign load, and let contaminated data pass as clean. Over-reporting shows up
in the retake and contamination counts; under-reporting shows up nowhere.
The host CPU claim is taken before the preflight gate, since waiting for a
quiet host and then blocking on the claim leaves the quiet verdict stale.
Everything observed reaches the console card, the CSV schema, and the JSON
object. That visibility is the point: a run that had to repair itself must say
so, or a heavily repaired result reads as a quieter machine than the one it
ran on.
Verified with ./scripts/test.sh on Darwin/aarch64 and in an Ubuntu 24.04
container on Linux/aarch64, and under qemu-system-aarch64 across four CPU
topologies. Core-scoped attribution asserts that it closed an observation
window, so the thread-CPU probe it depends on cannot fail silently. Two
assertions were checked against builds that defeat them: one with sibling
collection disabled fails the sibling assertion under cores=2,threads=2 and
passes without SMT, and one with the dilution check disabled fails the
fallback assertion while a task of the test's own process burns CPU. Retake
and pause are driven by a spawned process, because load raised inside this
process is its own CPU time and is subtracted by design.
845bdacawebsite/AGENTS.md governs hand-written pages under guide/**. The interference
section met none of its API-link requirements and several of its style rules.
Every public entity it introduced went unlinked: Interference, Placement,
Host_Lock, the three Interference_Response literals, Maximum_Retakes, Window,
Rewarm_Time, Maximum_Foreign_CPU_Percent, Include_Siblings, Require_Strict,
Attribution_Diluted, and Environment. The same page already links
CPU_Quiescence, Stable_Time, and Timeout, so the new section also read
inconsistently against its own neighbours.
Solution: Link the API entities and apply the three prescribed reviews
The three reviews ran in sequence and are labelled here, since separate review
agents were unavailable.
Technical. "Foreign load is host busy time minus this process's own CPU time"
held only without placement, because core-scoped attribution subtracts the
placed thread rather than the whole process; the sentence now says so, and the
placement paragraph states which CPUs the harness measures. "The re-warm is
required" overstated a configurable field, so the text names Rewarm_Time and
says what a zero value leaves behind. "When the difference exceeds the
configured limit" named neither the limit nor the compared quantity, which
also left Maximum_Foreign_CPU_Percent unlinked. Two harnesses that both pause
do not deadlock as claimed; they alternate between pausing and resuming.
Editorial. "This matters most" and "Without it" both referred back to
something unnamed. Both now name their subject.
Controlled language. "Sharpens attribution" was figurative and is now
"narrows attribution to the placed CPU and its SMT siblings". "A run that
repaired itself reports that fact" personified the run; the report is the
actor, and it states counts.
Verified with ./scripts/build-site.sh and the site checker, which passes 144
HTML files. All fourteen API links resolve to an existing anchor in the
generated GNATdoc output, and each target matches the entry the API search
index gives for that entity. No sentence in the section exceeds the 25-word
review signal.
edf788efThe interference section described three capabilities without saying that a
run acquires none of them unless it asks. BENCH 07 states that contract for
the preflight gate, and the page marks the default value of a choice where one
exists, as it does for Equal_Time and Balanced_Rounds. The new section did
neither, so a reader could not tell that enabling the watch alone changes
nothing about the collected data.
The claim file was also absent. The page named three environment variables but
not the one path that other tools must agree on, leaving the coordination
surface a click away in the convention document.
Solution: State the defaults, the claim path, and the budget interaction
Observe is now marked as the default response, in the form the page already
uses. The closing paragraph mirrors BENCH 07: the watch, placement, and the
host claim are disabled by default, so an existing run acquires no new wait or
failure condition. The opening sentence calls the watch optional where the
reader first meets it.
The claim paragraph names /tmp/host-cpu.lock, the Path field that overrides it
for one run, and the HOST_CPU_LOCK_PATH variable that overrides it for every
tool following the convention. An earlier draft named only the variable, which
implied it was the sole override; the technical review caught that against
Resolved_Path and Try_Acquire.
The Window row gains the limit that qualifies it. A window covers whole units,
so a window spanning many short samples can exceed Maximum_Retakes on its
first discard. Collection then continues under Observe and reports the
exhausted budget, which otherwise looks like the response never ran.
Numeric field defaults are deliberately not repeated here. No other section of
the page tabulates them, every field now links to its generated entry, and a
duplicated value is one that can go stale.
Verified with ./scripts/build-site.sh and the site checker, which passes 144
HTML files. All fifteen API links resolve to an existing anchor, and each
target matches the entry the API search index gives for that entity, including
the ambiguous Path simple name, which resolves to Host_Lock_Policy.Path. No
sentence in the section exceeds the 25-word review signal, and no entity is
linked twice.
4fbe5020recording_smoke drives two benchmarks from four tasks and asserts that the
slow benchmark's recorded median wall time exceeds the fast one's. The
separation comes from delay 0.000_2 against delay 0.000_8. A relative delay
is rounded up to the host timer tick, and on a Linux kernel with a coarse
tick both requests wake at the same boundary near 3 ms. A four-task probe
with no recorder involved shows the first iteration of each task honoring
the request and every later one landing on the same plateau:
0.0002 s: 323 3710 2829 2972 3069 2947 ... (us)
0.0008 s: 1167 3307 2482 3625 2793 3045 ... (us)
The two recorded medians then arrive at 2.98 ms and 2.99 ms, ordered by
scheduling noise alone. On Ubuntu 24.04 with GNAT 16.1.0 on aarch64 the
assertion fails about half the time: 9 of 20 runs on a clean checkout. macOS
resolves the two delays apart, which is why the test has held there. The
independent comparison checked two assertions below rests on the same
separation and fails with it.
Solution: Separate the distributions by work the workers perform
Each worker spins a volatile rotate-and-xor accumulator, 20_000 rounds for
the fast benchmark and twenty times that for the slow one, the idiom the
file's concurrent PMU-session block already uses. That is roughly 33 us
against 680 us, a twentyfold gap read by a nanosecond wall clock, so no
timer resolution enters the measurement. It also costs less than the
sleeping it replaces: the slow workers take about 14 ms rather than 60 ms.
The assertion stands, since the ordering is the invariant worth holding, but
its message now carries both medians so a failure on an unusual host reports
numbers rather than a verdict.
Verified on the failing environment, an Ubuntu 24.04 container with GNAT
16.1.0 on aarch64: 30 of 30 runs pass where HEAD fails 9 of 20 in the same
container, 20 of 20 under a two-CPU quota, and 10 of 10 with 24 competing
spinners on that quota. scripts/test.sh passes three times from a fresh
source export there and once on macOS. A calibration probe over 105 further
trials across those CPU configurations held the ratio between 14x and 30x
with no inversions and Relative_Change_Percent positive throughout.
ae5b52d7Flyology.Observability.Group_Snapshot carries thirty fields covering queue
depths, wait categories, and cumulative counters, but none of them reports how
much of an event loop's time was spent doing work. Pool imbalance is therefore
invisible from outside the process: a group with a deep ready queue and a group
that is idle produce the same evidence once their momentary queue depths match.
Made_Progress detects a stalled loop but not a saturated one, and its comment
already records why a per-dispatch clock read was rejected as the answer.
Solution: Time the poller wait a loop takes when nothing is ready
An event loop enters Wait_Batch only when its ready queue is empty, so the idle
path is both the exact complement of the loop's working time and a path a busy
loop never repeats. The scheduler stamps the wait before releasing the group
lock and closes it after reacquiring, keeping the in-progress wait in the group
so an observer adds it rather than charging a long block as busy time. Nothing
is added to dispatch, context switch, or any other hot path.
Group_Snapshot gains Uptime_Nanoseconds, Idle_Nanoseconds, and Idle_Waits, and
the runtime snapshot ABI moves to version 6. Idle time is clamped to uptime and
both come from one clock reading, so callers can always take the difference or
the ratio. tests/native/process_lifecycle_probe.c pins that ABI and its layout,
and moves with it.
The cost is measured rather than asserted. runtime_callback_bench adds
poller_idle_cycle for the instrumented path and monotonic_clock_read for the
unit of work it adds; benchmarks/idle_wait_rate reports the rate a loop
actually reaches, because the effect is far below what a differential timing
run resolves on this host. On macOS/AArch64 a reading costs about 20 ns, a
socket ping-pong reaches roughly 32,000 to 38,000 waits per second per loop,
and a loop doing nothing else reaches roughly 120,000 to 160,000: under 0.2%
and under 0.7% of a loop's time respectively.
Checks run: ./scripts/test.sh (pass, including the new
observability_utilization_smoke), ./scripts/docs.sh (pass, no new warnings),
./scripts/bench-runtime.sh.
6c4bfc3cStep 10 of the guide lists the readings that diagnose a group, but every one of
them describes queue contents. A reader who wants to know whether an event loop
is saturated has no entry to follow, and the utilization fields the runtime now
publishes appear only in README.md and the generated API reference.
Solution: Document loop utilization where the guide diagnoses a group
A new subsection explains what the runtime measures, why the subtraction is
always valid, and the two readings that look alike: a group whose tasks all
wait resembles a group with nothing to do, and a task that never suspends keeps
its loop out of the poller and reports as fully busy. A decision row places the
measure next to the queue-depth readings it qualifies. The cost paragraph names
the measured figures and the script that reproduces them, rather than asserting
that the accounting is cheap.
The worked example is compiled and run by tests/guide_utilization_example.adb.
The first draft of that example did not compile, because Counter is a modular
subtype and the subtraction needs a use type clause the guide had not shown. A
test main is the only check that keeps a printed example honest about the
visibility a reader must supply.
Checks run: ./scripts/test.sh (pass, including the new
guide_utilization_example), ./scripts/build-site.sh, and check-site.mjs over
the built site as CI runs it.
fb881640The website/AGENTS.md technical review is required for any changed capability,
limit, or timing claim, and running it on the new guide section found four
faults.
Two are wrong rather than unclear. The guide told a reader that `Counter` needs
a `use type` clause because the subtype is modular; the clause is needed
because the operators of a type declared in another package are not directly
visible, so the stated rule teaches the wrong thing. The guide also said an
event loop waits in its poller only when no task can run, but the loop polls
with a zero timeout while work is ready, and the same section counts that
polling as busy time.
The remaining two narrow a claim that had become broader than the code.
"Dispatching, polling with work ready, or running task code" reads as an
exhaustive account of a loop's busy time and omits timer promotion, file
submission, and cancellation processing. That wording appeared in the guide,
README.md, and the Group_Snapshot field documentation, so all three change
together. `Group_Snapshot` and `Snapshot` appeared only inside the code block
and were linked nowhere in prose, which the API-link rule does not allow.
Solution: Correct the two faults and narrow the two broad claims
The editorial and controlled-language passes contributed three further changes:
one repeated paragraph opening, "idle fraction" used before the section
introduces it, and "reading" carrying both a clock read and a snapshot.
Checks run: ./scripts/docs.sh (pass, no new warnings),
./scripts/build-site.sh with check-site.mjs over the built site,
and observability_utilization_smoke, guide_utilization_example, and
observability_smoke rebuilt and re-run.
bca8ad7fscripts/build-site.sh writes the complete site to build/site, but nothing
records how to serve it. Reading a guide page as it will be published means
inventing a static-server invocation each time, and picking a port that does
not collide with a development server already running.
Solution: Record a launch configuration that serves the built site
The entry serves build/site on port 8231. It reads the build output and does
not produce it, so run scripts/build-site.sh first and again after editing
anything under website/.
Checks run: served build/site through the entry and loaded the guide page.
104d406fThe macOS body queries the flat hw.l1dcachesize sysctl. On a heterogeneous
Apple silicon host that name reports the efficiency-core capacity, not the
performance-core capacity: one 12P/4E host reports 65536 bytes for the flat
name against 131072 for hw.perflevel0.l1dcachesize. Flyology_Cachelines.
L1_Data_Cache_Size therefore reports half the performance-core L1d capacity,
and L1_Data_Cache_Slots derives its slot count from that halved value.
The reader also assumed every queried name stores a 64-bit value. It read
each name into one 64-bit object and accepted a short reply, so a 32-bit
value was correct only because Darwin is little-endian and the object was
zero-initialized. The per-level names are 32-bit.
Solution: Prefer the highest performance level and read the reported width
macOS 12 and later describe a host whose cores are not identical as ordered
performance levels, where hw.perflevel0 is the highest-performing level. The
macOS body now queries hw.perflevel0.l1dcachesize and falls back to the flat
name only on a host that publishes no performance level, such as macOS 11 and
earlier or an Intel Mac. Querying hw.nperflevels first would add nothing: the
per-level name exists exactly when the host publishes performance levels.
Hardware_Cache_Line_Size is unchanged, because Darwin publishes no per-level
line size and both Apple silicon core types use the same line.
The public API keeps its Cache_Query_Result surface. Reporting per-level
geometry is a separate design question, and it should not ride along in a
detection correctness fix.
The sysctl reader moves into a new private Flyology_Cachelines.Macos child,
mirroring the existing Flyology_Cachelines.Linux split between mechanism and
policy. It requests each name's stored width before reading the value and
rejects any other width, so a 32-bit name, a 64-bit name, a string, and an
absent name are all handled without depending on byte order. Failed and
malformed queries still degrade to Unavailable rather than raising.
The GNATdoc comments on L1_Data_Cache_Size and L1_Data_Cache_Slots, the crate
README, and the website cachelines guide now state that a host whose cores are
not identical reports one core type, and which one each platform reports. A new
macOS detection test mirrors the Linux one: it exercises the sysctl reader
directly and checks that the reported capacity is the highest performance level
the host publishes.
Checks run: ./flyology_cachelines/scripts/test.sh (the native test now reports
131072 bytes of L1 data cache on a 12P/4E host, and the new macOS detection
test passes) and ./flyology_cachelines/scripts/docs.sh (zero GNATdoc warnings).
d3af8647Flyology_Cachelines.Linux_Testing asserts that both Detect_From_Sysconf and
Detect_From_Sysfs report a value. Only their composition is guaranteed:
glibc answers _SC_LEVEL1_DCACHE_SIZE and _SC_LEVEL1_DCACHE_LINESIZE from
CPUID on x86-64, but reports zero on aarch64, where sysfs is the only
mechanism. Detect already treats sysconf as optional and falls back to
sysfs, so the test is stricter than the code it covers.
The test therefore fails on aarch64 Linux with "sysconf cache detection
failed". CI runs ubuntu-latest on x86-64, where sysconf answers, so the
failure is invisible there. It reproduces on aarch64 Linux, which AGENTS.md
tells contributors to prefer on Apple silicon because it is native and
faster.
Solution: Require the composition and check each available result
Run now requires that at least one mechanism reports a value, and checks
consistency only for a mechanism that reported one. That is what the
production detector promises: it composes sysconf and sysfs, and returns
Unavailable only when neither answers.
Checks run: the crate suite on aarch64 Linux in a container, where the
detection test now passes, and on macOS, where it is not built.
04658ed7Flyology_Cachelines.Linux reads one L1 data-cache geometry: sysconf answers
for the calling CPU, and the sysfs fallback reads cpu0. A host whose cores
are not identical has more than one geometry, and neither mechanism says
which core the reported one belongs to, or that others exist.
macOS already publishes ordered performance levels, so the macOS body can
prefer the highest-performing one. Linux publishes per-CPU descriptions and
no single ordering, so the equivalent information has to be derived before
any public per-core-type query can work on both platforms.
Solution: Group CPUs into core classes and record what ordered them
Detect_Core_Classes enumerates the CPUs the host lists as present, reads
each one's L1 data-cache geometry and capacity, groups CPUs reporting
identical values into a class, and counts each class.
Classes are grouped on capacity and geometry together. Grouping on geometry
alone merges two core types that happen to share an L1 data-cache size,
which real designs do.
Ordering uses whichever key separates the classes, and Core_Classes reports
which one did, so a caller can tell an authoritative order from an inferred
one:
- Host_Reported: the kernel's per-CPU capacity values separated them.
Capacity is the scheduler's own measure of relative CPU performance and
accounts for microarchitecture and clock. Architectures selecting the
generic topology code publish it under cpuN/cpu_capacity.
- Inferred: no capacity separated them, so they are ordered by descending
L1 data-cache capacity. That holds on current hybrid parts and carries
no meaning on a host whose classes differ some other way. x86-64 does
not publish capacity, so its hybrid parts land here.
- Unordered: fewer than two classes, or nothing separated them.
Cores and CPUs are counted separately. SMT siblings share one L1 data cache,
so the cache's sharing list identifies which CPU represents a physical core.
Counting logical CPUs as cores overstates a 24-core host with two threads
per core by a factor of two.
Detection fails closed. A missing or malformed CPU list leaves only CPU 0 to
enumerate, enumeration is bounded, a CPU whose description cannot be read is
skipped, and a host reporting more classes than the table holds reports none
rather than a table that silently omits a class.
Detect_Core_Classes takes the CPU root as a parameter defaulted to the real
sysfs path. Grouping, ordering, and the SMT distinction cannot be reached on
a uniform test host, so the detection test drives them with committed
fixture trees: two classes separated only by capacity, two classes separated
only by geometry with the smaller listed first, SMT pairs sharing one cache,
and a CPU list with a gap.
Nothing calls Detect_Core_Classes outside the test yet. The public
per-core-type query, and the elaboration cost of walking every CPU, belong
to the change that adds that query.
Checks run: the crate suite on aarch64 Linux in a container and on macOS.
Detection was also replayed against captured CPU descriptions from a 48-CPU
x86-64 host with two threads per core, which reports 24 cores against 48
CPUs and no capacity, and from an 80-CPU aarch64 host, which reports 80
cores against 80 CPUs and a published capacity.
ba8438daFlyology_Cachelines reports one L1 data-cache capacity. A host that combines
core types has one per type, so a single value silently describes whichever
type the platform body happened to pick and gives a caller no way to ask
about the others, or to learn that others exist.
The two platforms also disagreed. macOS reported the highest performance
level, while Linux reported whichever cache its own mechanism described:
sysconf for the calling CPU, sysfs for cpu0. The same call therefore meant
different things on the two platforms.
Solution: Make the core class an explicit parameter of every cache query
Core_Class names one class of cores that report a single cache geometry.
Core_Class_Count reports how many the host distinguishes, Core_Class_Cores
and Core_Class_CPUs describe each one, and L1_Data_Cache_Size and
L1_Data_Cache_Slots take the class they describe. The parameter defaults to
Fastest_Core_Class, so existing calls keep compiling and now mean the same
thing on both platforms: the class the host ranks highest.
Core_Class_Ordering reports what produced that rank, because it is a host
fact on some platforms and the crate's inference on others. macOS performance
levels and the Linux per-CPU capacity values are Host_Reported. A host
publishing no rank is Inferred, ordered by descending L1 data-cache capacity.
Fewer than two classes is Unordered.
Cores and CPUs are reported separately. Sibling CPUs under simultaneous
multithreading share one core's L1 data cache, so a 24-core host with two
threads per core reports 24 cores and 48 CPUs in one class.
Hardware_Cache_Line_Size takes no class. macOS publishes no per-class line
size, and the core types of current heterogeneous parts share one line size,
so a per-class query would have one answer to give.
The host is now inspected on first use rather than during elaboration.
Describing every class means reading one description per CPU on Linux, which
measures 14 ms for 48 CPUs and 24 ms for 80. A program that links this crate
and never asks about the host should not pay that at startup. The inspection
is idempotent, so a race on the first query can duplicate work but cannot
produce a value neither caller computed; the ready flag is atomic and is set
after the result is stored.
Flyology_Cachelines.Platform now answers one Detect call describing the whole
host, and the root package body owns the single memoization. The two Linux
bodies share one translation and differ only in what they report when sysfs
describes nothing: the x86-64 body can still read the calling CPU through
CPUID. Flyology_Cachelines.Linux drops its own class and ordering types in
favour of the public ones, so the crate has one Core_Class.
The website cachelines guide is corrected alongside the API. Adding the Class
parameter changed the generated GNATdoc anchors for L1_Data_Cache_Size and
L1_Data_Cache_Slots, so the guide's links to them became dangling fragments;
both now point at the regenerated anchors. The guide also claimed detection
happens during package elaboration, which is no longer true. The new entities
are linked to their generated entries on first mention.
Checks run: the crate suite on macOS, where a 12P/4E host reports two
host-ordered classes of 131072 and 65536 bytes across 12 and 4 cores; on
aarch64 Linux in a container; and on an x86-64 Linux host with 24 cores and
two threads per core, which exercises the CPUID body and reports 49152 bytes
across 24 cores and 48 CPUs. GNATdoc builds with no warnings.
9782c81bFlyology_Cachelines reports L1 but not L2, so a caller sizing a working set
against the level it will actually spill into has nothing to read.
The flat sysctl this crate would naturally have used is wrong in the same way
hw.l1dcachesize was, and by a larger factor. On a 12P/4E Apple silicon host
hw.l2cachesize reports 4194304, the efficiency cluster's cache, against the
performance cluster's 16777216. Adding L2 before core classes existed would
have reproduced the original defect at four times the size instead of two.
Reporting a capacity alone would also mislead. L2 is rarely private: that same
host shares 16 MiB across six performance cores and 4 MiB across four
efficiency cores, so neither figure is a per-core budget.
Solution: Report L2 capacity and how many cores share it, per class
L2_Cache_Size and L2_Sharing_Cores take the same core class as the L1
queries. A sharing count of one means the cache is private to a core; a
larger count means the reported capacity is divided among that many cores.
The GNATdoc on each function points at the other, because reading either
alone invites the per-core mistake.
macOS reads hw.perflevelN.l2cachesize and hw.perflevelN.cpusperl2. Linux
reads the level 2 entry of each CPU's cache description, counting the CPUs
its shared_cpu_list names. Both counts arrive as logical CPUs and become core
counts through the class's own threads-per-core ratio, so a host with two
threads per core does not report twice the cores sharing a cache.
Parsing a CPU list needs more than its leading index, so the Linux body gains
List_Count, which sums the single indexes and inclusive ranges a list names.
Describing L2 costs no extra file reads: the level 2 entry is found in the
same pass over the cache indexes that already locates the level 1 data entry.
The website cachelines guide gains the same pairing, with both names linked
to their generated entries.
Checks run: the crate suite on all three supported targets. A 12P/4E macOS
host reports 16777216 bytes across 6 cores for its performance class and
4194304 across 4 for its efficiency class, and the macOS test asserts that
these come from the performance level rather than the flat name. An aarch64
Linux host reports 16777216 across 6 cores. An x86-64 Linux host with 24
cores and two threads per core reports 2097152 across 1 core, where sysfs
gives 2048K shared by CPUs "0,24"; that run also compiles the CPUID body,
which builds nowhere else. Fixtures cover a per-class shared L2, an L2 shared
across SMT siblings, and a private L2. GNATdoc builds with no warnings.
9782c81b → updated to 7be049fcindex 6e2db0ef1 source commit
7be049fcThe two OpenSSL install steps in the test job carried no bound, so a mirror
that stops answering leaves apt waiting rather than returning an error. On
2026-08-19 run 32297071503, job 96211136305 sat on "Install OpenSSL 3 on
Linux" for 32 minutes and had to be cancelled by hand; it was the only check
blocking PR #49. Its log shows apt-get update fetching noble-security
InRelease at 20:13:04 and producing no further output until cancellation. The
macOS half of the same job passed, so nothing was wrong with the tests.
Left alone the job would have run to GitHub's six-hour default. Nothing else
in either workflow was bounded either: the checkout, the Alire install, the
index configuration, the Java and TLA+ downloads, and the GNATdoc install all
reach the network under the same default, and no job in this repository
declared a timeout at all, unlike the sibling repositories.
Solution: Bound every job and every step that reaches the network
Each job now declares a timeout-minutes, and so does each step that fetches
something. The bounds are generous multiples of what healthy runs measure
over the last 60 successful runs of each workflow, so they can only fire on a
stall. Jobs: test observes at most 17 minutes on macOS and 11 on Linux and is
bounded at 60; proof observes 6 minutes and TLA+ 7, both bounded at 30; the
pages build observes 69 seconds and is bounded at 20, its deploy 10 seconds
and bounded at 15. Steps: everything network-touching is bounded at 10
minutes against observed maxima of 1 to 6 seconds, except the Alire install,
which downloads the toolchain, observes 100 seconds, and is bounded at 15.
A step timeout alone would only have converted the 32-minute hang into a
10-minute one, because the stall is inside a single apt-get update that never
returns. That command is now capped per attempt with timeout(1) and retried
twice with 15 and 30 second backoff, so one unresponsive mirror costs a
retry rather than the job. The install that follows is capped as well but not
retried: a package that cannot be resolved should fail on the first try. The
worst case is three 120-second attempts plus a 10-second kill grace, 45
seconds of backoff, and a 190-second install, which is 10m25s and lands
inside the 12-minute step bound, so the annotation naming the transient
mirror is what the log ends on rather than a bare cancellation.
GNU timeout is used directly rather than scripts/run-with-timeout.sh. That
helper exists for portability to macOS, and this step is Linux-only.
The TLA+ download gains --connect-timeout 30 and --max-time 120 alongside its
existing --retry 3, because curl retries a failed transfer but not a stalled
one, and --max-time resets per attempt. Four bounded attempts stay inside the
step's 10 minutes.
Build, test, proof, and model-checking steps keep no step bound. Their
duration is workload-dependent, a bound sized for them would be a performance
budget rather than a stall detector, and the job timeout already covers them.
Nothing about what is installed, or how anything is built or tested, changes.
Checks run: both workflows parse. The retry script was extracted from the
committed YAML and driven under bash -e against stub apt-get invocations for
five outcomes: first-try success runs one update and one install; two
failures then success runs three updates and installs; three failures exit 1
with the annotation and never install; an install failure propagates its exit
status without retrying; and exit 124, which is what timeout(1) reports when
it kills a stalled command, is treated as retryable. The real commands were
then run in an ubuntu:24.04 container on coreutils 9.4, the runner's version:
the bounded update succeeds, interrupting one mid-flight reports 124, a
retry after that kill still succeeds rather than inheriting a poisoned apt
state, and the bounded install still yields OpenSSL 3.0.13 through pkg-config.
The convention is recorded in AGENTS.md so it holds for new steps, and it
matches the job-level timeouts already used in flyology-http and
gnat-patches, whose install steps want the same treatment.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7be049fc → updated to a53da35dindex b55699621 source commit
a53da35dThe allocator crate is developed alongside the runtime in this repository and
released on the same development line, but the dependency on it is declared as
"*". Nothing stops a dependent from resolving an allocator release built for a
different line, and the index has been carrying a tighter constraint by hand to
compensate.
Solution: Constrain allocators to its development line
Declare the line the allocator crate actually publishes, so the constraint
travels with the manifest instead of being reapplied wherever it is indexed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
flyology_allocators ~0.1.0-dev0.1.0-devCaller-owned memory allocation algorithms for Ada
9a24bf68 → updated to 60de2c69index 8a8e9de51 source commit
60de2c69flyology.gpr imports flyology_allocators.gpr, and the manifest both declares
flyology_allocators as a dependency and pins it to the in-repo directory.
Alire honours pins only in a root crate's manifest, so a consumer resolving
flyology from the index never receives that dependency. Its solution omits
flyology_allocators entirely and gprbuild then fails with "imported project
file flyology_allocators.gpr not found". A crate whose only dependency is
flyology = "~0.1.1-dev" reproduces it in two lines.
Nothing in this repository reports the fault, because the pin satisfies the
dependency for every build performed here. The pin also hid two further
assumptions that only the in-repo directory could satisfy.
Solution: Resolve the allocators dependency from the index
Drop the pin so the declared dependency resolves the same way for this crate
and for its consumers. The published flyology_allocators 0.1.0-dev is built
from this repository at the same commit and the same subdirectory, so the
source reached is identical; only the path by which it is reached changes.
Configure the organization index in this repository's own workflows. They
carried the community index alone, because the pin meant no job here ever
resolved flyology_allocators from an index, and it is published only in the
organization index. Without that step alr reports an incomplete solution
naming flyology_allocators as missing.
Name the excluded documentation project through the imported project rather
than through a repository path. Documentation.Excluded_Project_Files listed
flyology_allocators/flyology_allocators.gpr, which resolved only while the
crate sat in this working tree; as a dependency its project lives in the
Alire build tree under a content hash. Flyology_Allocators'Project_Dir asks
the imported project where it is, so the exclusion holds wherever the
dependency is deployed and the allocator packages stay out of this crate's
generated API, where they would otherwise be documented twice.
Record the rule that produced the fault in the agent guide: no committed
pins in a crate manifest, alr pin in the consuming workspace for unpublished
work, and a declared dependency must resolve from the index. Name the
gprbuild error there too, since that is where the fault appears and it names
a project file rather than a crate.
60de2c69 → updated to 9782c81bindex 75a836be16 source commits
aeb10612The hero project map, the standalone catalogue, the library ledger, and
llms.txt each name every component. Flyology RDF publishes a website, a
guide, and three generated API references at rdf.flyology.org, but the name
appears nowhere in this repository. A reader who arrives at the site and
looks for what Flyology offers does not learn that the project exists.
Solution: Add the project to the hero map, catalogue, ledger, and llms.txt
flyology_rdf depends on flyology_iri only. It needs no runtime preparation
and starts no tasks, so it belongs with the standalone libraries. Every list
keeps its alphabetical order, so the entry sits between Flyology NUMA and
Flyology SIMD and the ledger indices shift from S07..S08 to S08..S09.
The ledger and llms.txt entries carry all three generated references, as the
Flyology Postgres entries already do, because the project site treats the
Notation3 and SPARQL APIs as first-class navigation.
The llms.txt project-structure summary also omitted Flyology Allocators and
Flyology NUMA, which the same file already listed further down. Both names
join the summary here.
Verified with the website-kit site checker, whose output is unchanged from
the pre-change baseline, and by rendering the pages: the hero, catalogue,
and ledger all name the project. The six linked URLs return 200.
2188ee0dThe figure caption claimed "One project, two adoption paths" above two cards
that already carry the same fact in their labels, so the caption restated
what the diagram shows. The two card subtitles were not parallel either:
"Core component" named a role while "No runtime dependency" named a
dependency, so neither pair member helped a reader compare the paths.
The component names below them were plain list items. A reader who found the
name that matched the problem had no way to reach the component from the
diagram and had to scroll to the catalogue to find the same name again.
Solution: Drop the caption, make the subtitles parallel, and link the names
The subtitles now read "Prepare the runtime" and "Add a crate", which state
the two adoption paths as the actions they are. The figure keeps an
accessible name through aria-label, and the figcaption rule leaves the
stylesheet with the element it styled.
Each component name becomes a link to its best available destination, which
is the destination the catalogue below already uses: the project site where
one exists, then the guide, then the generated API, then the source tree.
The pill moves from the list item to the anchor so the whole badge is the
hit target. Links open in a new tab and carry no underline, as requested, so
hover brightens the border and adds a faint fill instead. Each list states
the new-tab behavior once in its aria-label rather than repeating it in
twelve hidden spans.
Verified by rendering the page: every badge computes text-decoration-line
none and cursor pointer, and the hovered badge is distinguishable from its
neighbors. The site checker reports one new line against the baseline,
api/flyology_debug/ from the new Debug badge, which resolves once
scripts/docs.sh generates the GNATdoc output that this partial build omits.
5dfd5d36Two measurement runs that share a machine produce two wrong answers, and
neither run can tell that it happened. The problem is not confined to
benchmarks: a load generator, a soak test, or a profiler makes the machine
loud, and a benchmark needs it quiet. Both want the same thing from opposite
directions, and nothing lets them find each other.
Worse, two runs that each suspend collection when they detect interference
are symmetric controllers observing each other. Each pauses because the other
is loading the machine, both then observe quiet, both resume together, and
both immediately pause again.
Solution: Specify a host CPU lock convention and implement it
The convention is named for the resource rather than for benchmarking, so
that the tools which create load take the same claim as the tools which need
its absence. Conflict detection is flock and nothing else: liveness is the
lock itself, so a holder that dies releases with no cleanup path, no stale
registry entries, and no pid-reuse hazard.
A machine-wide claim takes LOCK_EX on the claim file; a claim on individual
CPUs takes LOCK_SH there and LOCK_EX on one file per CPU. Disjoint core-scoped
claims therefore proceed concurrently while a machine-wide claim excludes them
all, with no registry and no arbiter. Per-CPU claims are acquired in ascending
order, because a total order is what stops two callers with overlapping sets
from deadlocking on each other's partial claims.
The path is a single deterministic default rather than a writability-probing
fallback chain. A chain reintroduces the failure it was meant to prevent:
/run/lock is 1777 on Debian and root-owned 0755 on Red Hat derivatives, so a
root process and an unprivileged process on one machine would probe
differently, land on different files, and each conclude it holds the machine.
$TMPDIR is avoided for the same reason, being per-user on macOS.
File content is diagnostic and is never consulted to decide conflicts. It is
still specified and parseable, one key=value pair per line, because a reader
must be able to name the holder; cwd is included because tool and pid alone do
not distinguish one checkout or CI workspace from another. Readers open the
file without locking, since a shared lock would block behind the very holder
they are trying to identify, and must tolerate content that is empty, stale,
or half-written.
A claim is not proof of exclusivity, and the implementation does not present
it as one. systemd PrivateTmp= bind-mounts a per-service directory over /tmp,
so two services share every CPU and cannot see each other's claim files; that
case is detected from /proc/self/mountinfo and reported as namespace-scoped.
Separate containers are not detectable from inside and need an explicit shared
path. A path can be proven private but never proven machine-wide.
Only the C macros live in the native unit. GNAT's C_Variadic_2 convention
calls open() correctly even on Darwin/AArch64, where variadic arguments are
passed differently from fixed ones, so the protocol, the file grammar, and the
/proc reader are all Ada. The convention compiles on every GNAT this crate
supports, checked against 13.2.2 through 16.1.0 against an alire.toml floor of
13, so no compatibility shim is carried.
Verified by building the crate, and by taking claims on disk: the file matches
the published grammar, parses with awk -F=, is created 0666, and per-CPU files
are named beside the machine file.
13b71a5bCPU_Quiescence samples host counters once, before clock characterization, and
then never looks again. A machine that goes busy halfway through collection
produces a result that reads as clean, and nothing in the measurement, the
console card, or the machine-readable output says otherwise. The gate's own
documentation admits it "cannot prevent new load from arriving after
collection begins", and until now that was the end of the story.
The obvious repair is not available either. Host utilization cannot simply be
compared against a limit during collection the way the preflight does it,
because by then the benchmark thread saturates a core and every sample would
trip the limit.
Solution: Watch for foreign load during collection and say what was found
Foreign load is host busy time minus this process's own CPU time, so load the
benchmark itself creates is correctly not foreign. The estimate scales the
host busy ratio by wall time rather than converting ticks, so the platform
tick length never enters the arithmetic. Cross-checked against top on a loaded
machine, the two agree to within 0.1 percent.
Three responses form a ladder. Observe records and keeps everything. Retake
discards the contaminated window and collects it again. Pause waits for the
host to settle, re-warms, and then collects it again. The re-warm is not
politeness: a resumed run has cold caches, predictors, and frequency state, so
without it the first sample after a pause is the outlier the pause existed to
avoid.
Nothing adjusts a reported statistic. A skew factor derived from observed load
would be invented data, because the cost of interference depends on whether
the contention is an SMT sibling, a shared cache, memory bandwidth, or another
socket, and the same foreign share can mean no slowdown or a large one. Runs
are annotated or repaired, never corrected.
Observation happens between timed samples over windows of whole collection
units, where a unit is one sample, one comparison pair, or one balanced
multi-way round. A response therefore never splits the two halves of a pair
and never lands mid-round, where it would spread interference unevenly across
the cases a round exists to compare fairly. Windows carry a margin over the
configured minimum: sized to only just reach it, ordinary batch jitter drops
them below it and they silently degrade to observation. Host counters are
tick-based, so a window that still falls short is recorded but never acted on.
Discarding a window rolls back what the window changed. Per-sample values are
written by index and survive a retake intact, but the position tally of a
comparison pair and the process-telemetry summaries are running sums, so a
discarded pass gives them back. Without that, a run with eighteen retained
samples and twenty-four retakes would report elapsed time and an average CPU
share covering forty-two samples, most of which were thrown away.
Budgets degrade rather than abort. An exhausted retake or pause budget
continues under Observe and reports it, and paused time is excluded from
Maximum_Sampling_Time because waiting for the host is not collection.
Placement is optional and sharpens attribution: with a strict binding every
other CPU's busy time is foreign outright, and the placed CPU's share is its
busy time minus the placed thread's own. SMT siblings join the watched set,
because a process saturating the sibling of the placed CPU slows the
measurement while leaving the placed CPU's own share clean, which makes
sibling-blind core-scoped observation worse than host-wide observation rather
than better. Placement is Linux-only in practice; Apple Silicon implements no
thread affinity and rejects every request, so the Host_Control documentation
no longer describes Darwin placement as merely advisory.
Placement binds only the calling thread, so another thread of this process can
occupy a watched CPU, where its time is indistinguishable from foreign load.
Each window therefore reads process CPU as well as thread CPU, and their
difference bounds how much of the watched capacity those threads could account
for. Once that bound exceeds the configured foreign limit, the core-scoped
answer can no longer address the question the limit asks: the run drops to
host-wide observation, which is well defined for a multi-threaded process, and
reports Attribution_Diluted. Otherwise a scheduler benchmark run under
placement would spend its retake budget discarding samples over load it
created itself. The window that reveals the dilution is discarded rather than
recorded, because its value was computed under the attribution being
abandoned.
The direction of that fallback is deliberate. Core-scoped attribution
subtracts only the placed thread, which over-reports interference when other
threads share the watched CPUs; subtracting the whole process instead would
deduct time our threads spent on CPUs outside the watched set, under-report
foreign load, and let contaminated data pass as clean. Over-reporting shows up
in the retake and contamination counts; under-reporting shows up nowhere.
The host CPU claim is taken before the preflight gate, since waiting for a
quiet host and then blocking on the claim leaves the quiet verdict stale.
Everything observed reaches the console card, the CSV schema, and the JSON
object. That visibility is the point: a run that had to repair itself must say
so, or a heavily repaired result reads as a quieter machine than the one it
ran on.
Verified with ./scripts/test.sh on Darwin/aarch64 and in an Ubuntu 24.04
container on Linux/aarch64, and under qemu-system-aarch64 across four CPU
topologies. Core-scoped attribution asserts that it closed an observation
window, so the thread-CPU probe it depends on cannot fail silently. Two
assertions were checked against builds that defeat them: one with sibling
collection disabled fails the sibling assertion under cores=2,threads=2 and
passes without SMT, and one with the dilution check disabled fails the
fallback assertion while a task of the test's own process burns CPU. Retake
and pause are driven by a spawned process, because load raised inside this
process is its own CPU time and is subtracted by design.
845bdacawebsite/AGENTS.md governs hand-written pages under guide/**. The interference
section met none of its API-link requirements and several of its style rules.
Every public entity it introduced went unlinked: Interference, Placement,
Host_Lock, the three Interference_Response literals, Maximum_Retakes, Window,
Rewarm_Time, Maximum_Foreign_CPU_Percent, Include_Siblings, Require_Strict,
Attribution_Diluted, and Environment. The same page already links
CPU_Quiescence, Stable_Time, and Timeout, so the new section also read
inconsistently against its own neighbours.
Solution: Link the API entities and apply the three prescribed reviews
The three reviews ran in sequence and are labelled here, since separate review
agents were unavailable.
Technical. "Foreign load is host busy time minus this process's own CPU time"
held only without placement, because core-scoped attribution subtracts the
placed thread rather than the whole process; the sentence now says so, and the
placement paragraph states which CPUs the harness measures. "The re-warm is
required" overstated a configurable field, so the text names Rewarm_Time and
says what a zero value leaves behind. "When the difference exceeds the
configured limit" named neither the limit nor the compared quantity, which
also left Maximum_Foreign_CPU_Percent unlinked. Two harnesses that both pause
do not deadlock as claimed; they alternate between pausing and resuming.
Editorial. "This matters most" and "Without it" both referred back to
something unnamed. Both now name their subject.
Controlled language. "Sharpens attribution" was figurative and is now
"narrows attribution to the placed CPU and its SMT siblings". "A run that
repaired itself reports that fact" personified the run; the report is the
actor, and it states counts.
Verified with ./scripts/build-site.sh and the site checker, which passes 144
HTML files. All fourteen API links resolve to an existing anchor in the
generated GNATdoc output, and each target matches the entry the API search
index gives for that entity. No sentence in the section exceeds the 25-word
review signal.
edf788efThe interference section described three capabilities without saying that a
run acquires none of them unless it asks. BENCH 07 states that contract for
the preflight gate, and the page marks the default value of a choice where one
exists, as it does for Equal_Time and Balanced_Rounds. The new section did
neither, so a reader could not tell that enabling the watch alone changes
nothing about the collected data.
The claim file was also absent. The page named three environment variables but
not the one path that other tools must agree on, leaving the coordination
surface a click away in the convention document.
Solution: State the defaults, the claim path, and the budget interaction
Observe is now marked as the default response, in the form the page already
uses. The closing paragraph mirrors BENCH 07: the watch, placement, and the
host claim are disabled by default, so an existing run acquires no new wait or
failure condition. The opening sentence calls the watch optional where the
reader first meets it.
The claim paragraph names /tmp/host-cpu.lock, the Path field that overrides it
for one run, and the HOST_CPU_LOCK_PATH variable that overrides it for every
tool following the convention. An earlier draft named only the variable, which
implied it was the sole override; the technical review caught that against
Resolved_Path and Try_Acquire.
The Window row gains the limit that qualifies it. A window covers whole units,
so a window spanning many short samples can exceed Maximum_Retakes on its
first discard. Collection then continues under Observe and reports the
exhausted budget, which otherwise looks like the response never ran.
Numeric field defaults are deliberately not repeated here. No other section of
the page tabulates them, every field now links to its generated entry, and a
duplicated value is one that can go stale.
Verified with ./scripts/build-site.sh and the site checker, which passes 144
HTML files. All fifteen API links resolve to an existing anchor, and each
target matches the entry the API search index gives for that entity, including
the ambiguous Path simple name, which resolves to Host_Lock_Policy.Path. No
sentence in the section exceeds the 25-word review signal, and no entity is
linked twice.
4fbe5020recording_smoke drives two benchmarks from four tasks and asserts that the
slow benchmark's recorded median wall time exceeds the fast one's. The
separation comes from delay 0.000_2 against delay 0.000_8. A relative delay
is rounded up to the host timer tick, and on a Linux kernel with a coarse
tick both requests wake at the same boundary near 3 ms. A four-task probe
with no recorder involved shows the first iteration of each task honoring
the request and every later one landing on the same plateau:
0.0002 s: 323 3710 2829 2972 3069 2947 ... (us)
0.0008 s: 1167 3307 2482 3625 2793 3045 ... (us)
The two recorded medians then arrive at 2.98 ms and 2.99 ms, ordered by
scheduling noise alone. On Ubuntu 24.04 with GNAT 16.1.0 on aarch64 the
assertion fails about half the time: 9 of 20 runs on a clean checkout. macOS
resolves the two delays apart, which is why the test has held there. The
independent comparison checked two assertions below rests on the same
separation and fails with it.
Solution: Separate the distributions by work the workers perform
Each worker spins a volatile rotate-and-xor accumulator, 20_000 rounds for
the fast benchmark and twenty times that for the slow one, the idiom the
file's concurrent PMU-session block already uses. That is roughly 33 us
against 680 us, a twentyfold gap read by a nanosecond wall clock, so no
timer resolution enters the measurement. It also costs less than the
sleeping it replaces: the slow workers take about 14 ms rather than 60 ms.
The assertion stands, since the ordering is the invariant worth holding, but
its message now carries both medians so a failure on an unusual host reports
numbers rather than a verdict.
Verified on the failing environment, an Ubuntu 24.04 container with GNAT
16.1.0 on aarch64: 30 of 30 runs pass where HEAD fails 9 of 20 in the same
container, 20 of 20 under a two-CPU quota, and 10 of 10 with 24 competing
spinners on that quota. scripts/test.sh passes three times from a fresh
source export there and once on macOS. A calibration probe over 105 further
trials across those CPU configurations held the ratio between 14x and 30x
with no inversions and Relative_Change_Percent positive throughout.
ae5b52d7Flyology.Observability.Group_Snapshot carries thirty fields covering queue
depths, wait categories, and cumulative counters, but none of them reports how
much of an event loop's time was spent doing work. Pool imbalance is therefore
invisible from outside the process: a group with a deep ready queue and a group
that is idle produce the same evidence once their momentary queue depths match.
Made_Progress detects a stalled loop but not a saturated one, and its comment
already records why a per-dispatch clock read was rejected as the answer.
Solution: Time the poller wait a loop takes when nothing is ready
An event loop enters Wait_Batch only when its ready queue is empty, so the idle
path is both the exact complement of the loop's working time and a path a busy
loop never repeats. The scheduler stamps the wait before releasing the group
lock and closes it after reacquiring, keeping the in-progress wait in the group
so an observer adds it rather than charging a long block as busy time. Nothing
is added to dispatch, context switch, or any other hot path.
Group_Snapshot gains Uptime_Nanoseconds, Idle_Nanoseconds, and Idle_Waits, and
the runtime snapshot ABI moves to version 6. Idle time is clamped to uptime and
both come from one clock reading, so callers can always take the difference or
the ratio. tests/native/process_lifecycle_probe.c pins that ABI and its layout,
and moves with it.
The cost is measured rather than asserted. runtime_callback_bench adds
poller_idle_cycle for the instrumented path and monotonic_clock_read for the
unit of work it adds; benchmarks/idle_wait_rate reports the rate a loop
actually reaches, because the effect is far below what a differential timing
run resolves on this host. On macOS/AArch64 a reading costs about 20 ns, a
socket ping-pong reaches roughly 32,000 to 38,000 waits per second per loop,
and a loop doing nothing else reaches roughly 120,000 to 160,000: under 0.2%
and under 0.7% of a loop's time respectively.
Checks run: ./scripts/test.sh (pass, including the new
observability_utilization_smoke), ./scripts/docs.sh (pass, no new warnings),
./scripts/bench-runtime.sh.
6c4bfc3cStep 10 of the guide lists the readings that diagnose a group, but every one of
them describes queue contents. A reader who wants to know whether an event loop
is saturated has no entry to follow, and the utilization fields the runtime now
publishes appear only in README.md and the generated API reference.
Solution: Document loop utilization where the guide diagnoses a group
A new subsection explains what the runtime measures, why the subtraction is
always valid, and the two readings that look alike: a group whose tasks all
wait resembles a group with nothing to do, and a task that never suspends keeps
its loop out of the poller and reports as fully busy. A decision row places the
measure next to the queue-depth readings it qualifies. The cost paragraph names
the measured figures and the script that reproduces them, rather than asserting
that the accounting is cheap.
The worked example is compiled and run by tests/guide_utilization_example.adb.
The first draft of that example did not compile, because Counter is a modular
subtype and the subtraction needs a use type clause the guide had not shown. A
test main is the only check that keeps a printed example honest about the
visibility a reader must supply.
Checks run: ./scripts/test.sh (pass, including the new
guide_utilization_example), ./scripts/build-site.sh, and check-site.mjs over
the built site as CI runs it.
fb881640The website/AGENTS.md technical review is required for any changed capability,
limit, or timing claim, and running it on the new guide section found four
faults.
Two are wrong rather than unclear. The guide told a reader that `Counter` needs
a `use type` clause because the subtype is modular; the clause is needed
because the operators of a type declared in another package are not directly
visible, so the stated rule teaches the wrong thing. The guide also said an
event loop waits in its poller only when no task can run, but the loop polls
with a zero timeout while work is ready, and the same section counts that
polling as busy time.
The remaining two narrow a claim that had become broader than the code.
"Dispatching, polling with work ready, or running task code" reads as an
exhaustive account of a loop's busy time and omits timer promotion, file
submission, and cancellation processing. That wording appeared in the guide,
README.md, and the Group_Snapshot field documentation, so all three change
together. `Group_Snapshot` and `Snapshot` appeared only inside the code block
and were linked nowhere in prose, which the API-link rule does not allow.
Solution: Correct the two faults and narrow the two broad claims
The editorial and controlled-language passes contributed three further changes:
one repeated paragraph opening, "idle fraction" used before the section
introduces it, and "reading" carrying both a clock read and a snapshot.
Checks run: ./scripts/docs.sh (pass, no new warnings),
./scripts/build-site.sh with check-site.mjs over the built site,
and observability_utilization_smoke, guide_utilization_example, and
observability_smoke rebuilt and re-run.
bca8ad7fscripts/build-site.sh writes the complete site to build/site, but nothing
records how to serve it. Reading a guide page as it will be published means
inventing a static-server invocation each time, and picking a port that does
not collide with a development server already running.
Solution: Record a launch configuration that serves the built site
The entry serves build/site on port 8231. It reads the build output and does
not produce it, so run scripts/build-site.sh first and again after editing
anything under website/.
Checks run: served build/site through the entry and loaded the guide page.
104d406fThe macOS body queries the flat hw.l1dcachesize sysctl. On a heterogeneous
Apple silicon host that name reports the efficiency-core capacity, not the
performance-core capacity: one 12P/4E host reports 65536 bytes for the flat
name against 131072 for hw.perflevel0.l1dcachesize. Flyology_Cachelines.
L1_Data_Cache_Size therefore reports half the performance-core L1d capacity,
and L1_Data_Cache_Slots derives its slot count from that halved value.
The reader also assumed every queried name stores a 64-bit value. It read
each name into one 64-bit object and accepted a short reply, so a 32-bit
value was correct only because Darwin is little-endian and the object was
zero-initialized. The per-level names are 32-bit.
Solution: Prefer the highest performance level and read the reported width
macOS 12 and later describe a host whose cores are not identical as ordered
performance levels, where hw.perflevel0 is the highest-performing level. The
macOS body now queries hw.perflevel0.l1dcachesize and falls back to the flat
name only on a host that publishes no performance level, such as macOS 11 and
earlier or an Intel Mac. Querying hw.nperflevels first would add nothing: the
per-level name exists exactly when the host publishes performance levels.
Hardware_Cache_Line_Size is unchanged, because Darwin publishes no per-level
line size and both Apple silicon core types use the same line.
The public API keeps its Cache_Query_Result surface. Reporting per-level
geometry is a separate design question, and it should not ride along in a
detection correctness fix.
The sysctl reader moves into a new private Flyology_Cachelines.Macos child,
mirroring the existing Flyology_Cachelines.Linux split between mechanism and
policy. It requests each name's stored width before reading the value and
rejects any other width, so a 32-bit name, a 64-bit name, a string, and an
absent name are all handled without depending on byte order. Failed and
malformed queries still degrade to Unavailable rather than raising.
The GNATdoc comments on L1_Data_Cache_Size and L1_Data_Cache_Slots, the crate
README, and the website cachelines guide now state that a host whose cores are
not identical reports one core type, and which one each platform reports. A new
macOS detection test mirrors the Linux one: it exercises the sysctl reader
directly and checks that the reported capacity is the highest performance level
the host publishes.
Checks run: ./flyology_cachelines/scripts/test.sh (the native test now reports
131072 bytes of L1 data cache on a 12P/4E host, and the new macOS detection
test passes) and ./flyology_cachelines/scripts/docs.sh (zero GNATdoc warnings).
d3af8647Flyology_Cachelines.Linux_Testing asserts that both Detect_From_Sysconf and
Detect_From_Sysfs report a value. Only their composition is guaranteed:
glibc answers _SC_LEVEL1_DCACHE_SIZE and _SC_LEVEL1_DCACHE_LINESIZE from
CPUID on x86-64, but reports zero on aarch64, where sysfs is the only
mechanism. Detect already treats sysconf as optional and falls back to
sysfs, so the test is stricter than the code it covers.
The test therefore fails on aarch64 Linux with "sysconf cache detection
failed". CI runs ubuntu-latest on x86-64, where sysconf answers, so the
failure is invisible there. It reproduces on aarch64 Linux, which AGENTS.md
tells contributors to prefer on Apple silicon because it is native and
faster.
Solution: Require the composition and check each available result
Run now requires that at least one mechanism reports a value, and checks
consistency only for a mechanism that reported one. That is what the
production detector promises: it composes sysconf and sysfs, and returns
Unavailable only when neither answers.
Checks run: the crate suite on aarch64 Linux in a container, where the
detection test now passes, and on macOS, where it is not built.
04658ed7Flyology_Cachelines.Linux reads one L1 data-cache geometry: sysconf answers
for the calling CPU, and the sysfs fallback reads cpu0. A host whose cores
are not identical has more than one geometry, and neither mechanism says
which core the reported one belongs to, or that others exist.
macOS already publishes ordered performance levels, so the macOS body can
prefer the highest-performing one. Linux publishes per-CPU descriptions and
no single ordering, so the equivalent information has to be derived before
any public per-core-type query can work on both platforms.
Solution: Group CPUs into core classes and record what ordered them
Detect_Core_Classes enumerates the CPUs the host lists as present, reads
each one's L1 data-cache geometry and capacity, groups CPUs reporting
identical values into a class, and counts each class.
Classes are grouped on capacity and geometry together. Grouping on geometry
alone merges two core types that happen to share an L1 data-cache size,
which real designs do.
Ordering uses whichever key separates the classes, and Core_Classes reports
which one did, so a caller can tell an authoritative order from an inferred
one:
- Host_Reported: the kernel's per-CPU capacity values separated them.
Capacity is the scheduler's own measure of relative CPU performance and
accounts for microarchitecture and clock. Architectures selecting the
generic topology code publish it under cpuN/cpu_capacity.
- Inferred: no capacity separated them, so they are ordered by descending
L1 data-cache capacity. That holds on current hybrid parts and carries
no meaning on a host whose classes differ some other way. x86-64 does
not publish capacity, so its hybrid parts land here.
- Unordered: fewer than two classes, or nothing separated them.
Cores and CPUs are counted separately. SMT siblings share one L1 data cache,
so the cache's sharing list identifies which CPU represents a physical core.
Counting logical CPUs as cores overstates a 24-core host with two threads
per core by a factor of two.
Detection fails closed. A missing or malformed CPU list leaves only CPU 0 to
enumerate, enumeration is bounded, a CPU whose description cannot be read is
skipped, and a host reporting more classes than the table holds reports none
rather than a table that silently omits a class.
Detect_Core_Classes takes the CPU root as a parameter defaulted to the real
sysfs path. Grouping, ordering, and the SMT distinction cannot be reached on
a uniform test host, so the detection test drives them with committed
fixture trees: two classes separated only by capacity, two classes separated
only by geometry with the smaller listed first, SMT pairs sharing one cache,
and a CPU list with a gap.
Nothing calls Detect_Core_Classes outside the test yet. The public
per-core-type query, and the elaboration cost of walking every CPU, belong
to the change that adds that query.
Checks run: the crate suite on aarch64 Linux in a container and on macOS.
Detection was also replayed against captured CPU descriptions from a 48-CPU
x86-64 host with two threads per core, which reports 24 cores against 48
CPUs and no capacity, and from an 80-CPU aarch64 host, which reports 80
cores against 80 CPUs and a published capacity.
ba8438daFlyology_Cachelines reports one L1 data-cache capacity. A host that combines
core types has one per type, so a single value silently describes whichever
type the platform body happened to pick and gives a caller no way to ask
about the others, or to learn that others exist.
The two platforms also disagreed. macOS reported the highest performance
level, while Linux reported whichever cache its own mechanism described:
sysconf for the calling CPU, sysfs for cpu0. The same call therefore meant
different things on the two platforms.
Solution: Make the core class an explicit parameter of every cache query
Core_Class names one class of cores that report a single cache geometry.
Core_Class_Count reports how many the host distinguishes, Core_Class_Cores
and Core_Class_CPUs describe each one, and L1_Data_Cache_Size and
L1_Data_Cache_Slots take the class they describe. The parameter defaults to
Fastest_Core_Class, so existing calls keep compiling and now mean the same
thing on both platforms: the class the host ranks highest.
Core_Class_Ordering reports what produced that rank, because it is a host
fact on some platforms and the crate's inference on others. macOS performance
levels and the Linux per-CPU capacity values are Host_Reported. A host
publishing no rank is Inferred, ordered by descending L1 data-cache capacity.
Fewer than two classes is Unordered.
Cores and CPUs are reported separately. Sibling CPUs under simultaneous
multithreading share one core's L1 data cache, so a 24-core host with two
threads per core reports 24 cores and 48 CPUs in one class.
Hardware_Cache_Line_Size takes no class. macOS publishes no per-class line
size, and the core types of current heterogeneous parts share one line size,
so a per-class query would have one answer to give.
The host is now inspected on first use rather than during elaboration.
Describing every class means reading one description per CPU on Linux, which
measures 14 ms for 48 CPUs and 24 ms for 80. A program that links this crate
and never asks about the host should not pay that at startup. The inspection
is idempotent, so a race on the first query can duplicate work but cannot
produce a value neither caller computed; the ready flag is atomic and is set
after the result is stored.
Flyology_Cachelines.Platform now answers one Detect call describing the whole
host, and the root package body owns the single memoization. The two Linux
bodies share one translation and differ only in what they report when sysfs
describes nothing: the x86-64 body can still read the calling CPU through
CPUID. Flyology_Cachelines.Linux drops its own class and ordering types in
favour of the public ones, so the crate has one Core_Class.
The website cachelines guide is corrected alongside the API. Adding the Class
parameter changed the generated GNATdoc anchors for L1_Data_Cache_Size and
L1_Data_Cache_Slots, so the guide's links to them became dangling fragments;
both now point at the regenerated anchors. The guide also claimed detection
happens during package elaboration, which is no longer true. The new entities
are linked to their generated entries on first mention.
Checks run: the crate suite on macOS, where a 12P/4E host reports two
host-ordered classes of 131072 and 65536 bytes across 12 and 4 cores; on
aarch64 Linux in a container; and on an x86-64 Linux host with 24 cores and
two threads per core, which exercises the CPUID body and reports 49152 bytes
across 24 cores and 48 CPUs. GNATdoc builds with no warnings.
9782c81bFlyology_Cachelines reports L1 but not L2, so a caller sizing a working set
against the level it will actually spill into has nothing to read.
The flat sysctl this crate would naturally have used is wrong in the same way
hw.l1dcachesize was, and by a larger factor. On a 12P/4E Apple silicon host
hw.l2cachesize reports 4194304, the efficiency cluster's cache, against the
performance cluster's 16777216. Adding L2 before core classes existed would
have reproduced the original defect at four times the size instead of two.
Reporting a capacity alone would also mislead. L2 is rarely private: that same
host shares 16 MiB across six performance cores and 4 MiB across four
efficiency cores, so neither figure is a per-core budget.
Solution: Report L2 capacity and how many cores share it, per class
L2_Cache_Size and L2_Sharing_Cores take the same core class as the L1
queries. A sharing count of one means the cache is private to a core; a
larger count means the reported capacity is divided among that many cores.
The GNATdoc on each function points at the other, because reading either
alone invites the per-core mistake.
macOS reads hw.perflevelN.l2cachesize and hw.perflevelN.cpusperl2. Linux
reads the level 2 entry of each CPU's cache description, counting the CPUs
its shared_cpu_list names. Both counts arrive as logical CPUs and become core
counts through the class's own threads-per-core ratio, so a host with two
threads per core does not report twice the cores sharing a cache.
Parsing a CPU list needs more than its leading index, so the Linux body gains
List_Count, which sums the single indexes and inclusive ranges a list names.
Describing L2 costs no extra file reads: the level 2 entry is found in the
same pass over the cache indexes that already locates the level 1 data entry.
The website cachelines guide gains the same pairing, with both names linked
to their generated entries.
Checks run: the crate suite on all three supported targets. A 12P/4E macOS
host reports 16777216 bytes across 6 cores for its performance class and
4194304 across 4 for its efficiency class, and the macOS test asserts that
these come from the performance level rather than the flat name. An aarch64
Linux host reports 16777216 across 6 cores. An x86-64 Linux host with 24
cores and two threads per core reports 2097152 across 1 core, where sysfs
gives 2048K shared by CPUs "0,24"; that run also compiles the CPUID body,
which builds nowhere else. Fixtures cover a per-class shared L2, an L2 shared
across SMT siblings, and a private L2. GNATdoc builds with no warnings.
9782c81b → updated to 7be049fcindex 6e2db0ef1 source commit
7be049fcThe two OpenSSL install steps in the test job carried no bound, so a mirror
that stops answering leaves apt waiting rather than returning an error. On
2026-08-19 run 32297071503, job 96211136305 sat on "Install OpenSSL 3 on
Linux" for 32 minutes and had to be cancelled by hand; it was the only check
blocking PR #49. Its log shows apt-get update fetching noble-security
InRelease at 20:13:04 and producing no further output until cancellation. The
macOS half of the same job passed, so nothing was wrong with the tests.
Left alone the job would have run to GitHub's six-hour default. Nothing else
in either workflow was bounded either: the checkout, the Alire install, the
index configuration, the Java and TLA+ downloads, and the GNATdoc install all
reach the network under the same default, and no job in this repository
declared a timeout at all, unlike the sibling repositories.
Solution: Bound every job and every step that reaches the network
Each job now declares a timeout-minutes, and so does each step that fetches
something. The bounds are generous multiples of what healthy runs measure
over the last 60 successful runs of each workflow, so they can only fire on a
stall. Jobs: test observes at most 17 minutes on macOS and 11 on Linux and is
bounded at 60; proof observes 6 minutes and TLA+ 7, both bounded at 30; the
pages build observes 69 seconds and is bounded at 20, its deploy 10 seconds
and bounded at 15. Steps: everything network-touching is bounded at 10
minutes against observed maxima of 1 to 6 seconds, except the Alire install,
which downloads the toolchain, observes 100 seconds, and is bounded at 15.
A step timeout alone would only have converted the 32-minute hang into a
10-minute one, because the stall is inside a single apt-get update that never
returns. That command is now capped per attempt with timeout(1) and retried
twice with 15 and 30 second backoff, so one unresponsive mirror costs a
retry rather than the job. The install that follows is capped as well but not
retried: a package that cannot be resolved should fail on the first try. The
worst case is three 120-second attempts plus a 10-second kill grace, 45
seconds of backoff, and a 190-second install, which is 10m25s and lands
inside the 12-minute step bound, so the annotation naming the transient
mirror is what the log ends on rather than a bare cancellation.
GNU timeout is used directly rather than scripts/run-with-timeout.sh. That
helper exists for portability to macOS, and this step is Linux-only.
The TLA+ download gains --connect-timeout 30 and --max-time 120 alongside its
existing --retry 3, because curl retries a failed transfer but not a stalled
one, and --max-time resets per attempt. Four bounded attempts stay inside the
step's 10 minutes.
Build, test, proof, and model-checking steps keep no step bound. Their
duration is workload-dependent, a bound sized for them would be a performance
budget rather than a stall detector, and the job timeout already covers them.
Nothing about what is installed, or how anything is built or tested, changes.
Checks run: both workflows parse. The retry script was extracted from the
committed YAML and driven under bash -e against stub apt-get invocations for
five outcomes: first-try success runs one update and one install; two
failures then success runs three updates and installs; three failures exit 1
with the annotation and never install; an install failure propagates its exit
status without retrying; and exit 124, which is what timeout(1) reports when
it kills a stalled command, is treated as retryable. The real commands were
then run in an ubuntu:24.04 container on coreutils 9.4, the runner's version:
the bounded update succeeds, interrupting one mid-flight reports 124, a
retry after that kill still succeeds rather than inheriting a poisoned apt
state, and the bounded install still yields OpenSSL 3.0.13 through pkg-config.
The convention is recorded in AGENTS.md so it holds for new steps, and it
matches the job-level timeouts already used in flyology-http and
gnat-patches, whose install steps want the same treatment.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7be049fc → updated to a53da35dindex b55699621 source commit
a53da35dThe allocator crate is developed alongside the runtime in this repository and
released on the same development line, but the dependency on it is declared as
"*". Nothing stops a dependent from resolving an allocator release built for a
different line, and the index has been carrying a tighter constraint by hand to
compensate.
Solution: Constrain allocators to its development line
Declare the line the allocator crate actually publishes, so the constraint
travels with the manifest instead of being reapplied wherever it is indexed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.1.1-devAdaptive nanosecond-scale benchmarking for Ada
91d6a0f6 → updated to 9a24bf68index 510cfcf639 source commits
40849230The functional loopback checks use retry intervals too narrow for occasional macOS hosted-runner stalls. An early client exception also waits behind cancellation coordination and loses the operation that failed, leaving only a generic lifecycle assertion.
Solution: tolerate stalls and report DNS smoke stages
Use functional smoke deadlines that accommodate runner scheduling, let coordination observe early client completion, and report the execution model and operation stage for unexpected exceptions. The dedicated DNS resilience smoke retains narrow deadline coverage.
347604c6Session logs are distributed across project checkouts, worktrees, sister
repositories, and nested agent sessions. Their token counters also contain
cached input, repeated cumulative records, inherited history, and duplicated
Claude message entries, making straightforward aggregation inaccurate.
Solution: add a portable incremental AI usage audit
Add a self-contained PEP 723 Python script that discovers the workspace from
its containing repository and scans associated Codex and Claude Code logs,
including worktrees, subagents, and workflows.
Deduplicate cumulative and inherited records, categorize base input, cache
reads and writes, output and reasoning tokens, and estimate API-style costs
using model-specific pricing. Persist JSON cursors and daily aggregates so
subsequent runs process only new log records.
Provide summary, daily, weekly, provider, and model breakdowns with JSON
output, date filtering, configurable paths and timezone, state rebuilding,
and integrity diagnostics.
73925055The landing page, navigation, guide index, and machine-readable documentation center Flyology Runtime while standalone libraries share the project identity without depending on it. Visitors cannot reliably distinguish the runtime core, runtime-dependent protocol libraries, and independently adoptable components, and several component documentation paths are missing or placed under the wrong section.
Solution: Present Flyology as the project and Runtime as its core
Reorganize the landing page around the project umbrella, add focused Runtime and Libraries pages, move Guide, Architecture, and the main API under Runtime, separate standalone documentation from the Runtime guide, link each published component Guide and API, fix the scrollable guide topic rail, and align README, product, design, journal, build assertions, and llms.txt with the same dependency model.
6cb6887fThe redesigned project pages repeat the experimental label in several nearby locations while the curated Libraries directory has no path to the complete package index. Readers see redundant maturity warnings but cannot easily reach version, dependency, availability, and compiler-build records for every published package.
Solution: Clarify maturity guidance and link the complete crate index
Keep experimental status in the primary explanatory context, make constraints and adoption guidance more direct, and link crates.flyology.org from the Libraries introduction, footer, and llms.txt documentation map.
4738d656Only the Libraries page exposes the complete package catalogue in its footer. Readers arriving on Runtime, Guide, Architecture, Journal, or the project overview have no equivalent utility path to package versions, dependencies, and availability records.
Solution: Add the crate index to every shared footer
Place the Crate index utility link first in each hand-written footer, including the journal entry template, while retaining every page-specific navigation link.
5b7c3925Check_Parent_Cancellation_Is_Prompt in tests/task_scope_faults_smoke requires
every one of 16 rounds to propagate parent cancellation within 5 ms.
Propagation crosses two lightweight-task wakes, and a contended shared runner
can deschedule either of them for milliseconds, so the check reports host
scheduling as often as it reports the mechanism. CI run 32100324387 failed
this assertion on its first attempt and passed on a rerun of the identical
commit 71df9ed, whose only change is .github/workflows/pages.yml. Locally,
96 spinning processes on 16 cores plus nice -n 20 reproduce the failure in
2 of 30 runs, and 1280 rounds measured under that load put the median at
78 us with 0.9 per cent of rounds past 5 ms: the mechanism is prompt and the
bound is reporting the host. A bare pragma Assert also names only a source
line, so a real regression is indistinguishable from a stalled round.
Solution: Require most rounds to be prompt, bound the rest, and report them
The check now measures all 16 rounds, requires at least 10 of them within
1 ms, and requires no round past a 0.5 s sanity ceiling. That is a stronger
claim about the mechanism rather than a weaker one. The retired 10 ms polling
monitor spreads its latency across its period and places about 1.6 of 16
rounds inside 1 ms, so it clears the new bar about once in two million runs
against once in sixty-five thousand for the old whole-round 5 ms bound;
reinstating that monitor put 0 of 16 rounds inside 1 ms and failed the new
check on all five idle runs. Under the load that failed the old check in
11 per cent of runs, the new check passed 60 of 60. A failing round set is
printed to standard error so the next failure names the rounds and their
latencies instead of only the assertion line.
857820c4Problem: Cancellation promptness is asserted as a per-round wall-clock bound
5dfa0d25The site check that validates local links and fragment targets runs only in
the Pages workflow, which triggers on a push to main and on manual dispatch.
No pull-request workflow builds the site, and ci.yml has no site job, so a
change that breaks a link passes every check a reviewer sees, merges, and
then fails the Pages build on main. The site stops publishing at the point
where the mistake is hardest to attribute.
The hand-written pages carry 457 links into generated GNATdoc entries across
eleven guide and architecture pages. GNATdoc names each entry by a hash of
the declaration, so changing a public subprogram's parameters gives it a new
anchor and silently strands every hand-written link to it. Editing the
surrounding documentation comment does not, which makes the failure easy to
miss: prose edits are safe, and a signature change quietly is not.
Solution: Build and check the site on pull requests
The Pages workflow now also runs on pull_request. The build job is
unchanged for a push, and on a pull request it builds the site and runs the
same link check without touching Pages: configuring Pages, uploading the
artifact, and the deploy job are all skipped. A pull request therefore needs
no Pages permission, which also keeps the job usable from a fork.
Concurrency now groups per ref. One group for every event serialized
unrelated pull requests behind each other and behind a deploy. Pushes to main
still share one group and are still never cancelled; a pull request cancels
its own superseded runs.
This adds a toolchain and GNATdoc build to each pull request, which is the
cost of resolving generated anchors: the check can only confirm a fragment
exists by generating the page that defines it.
Checks run: the workflow parses, has no tabs, guards exactly the three
Pages-specific steps, and keeps the link check unguarded. check-site.mjs was
confirmed to catch this class of breakage against a page carrying one stale
API fragment, where it reports "missing fragment target" and exits non-zero.
This pull request triggers the new job, so its own run is the end-to-end
check.
252f4fd7A machine with more than one processor package attaches memory to each of
them, and memory attached to the package a thread runs on is reached faster
than memory attached to another. Nothing in this repository reports that
structure. Ada itself does not describe it either: System.Multiprocessors
covers processors and dispatching, and the language has no notion of a
memory node at all.
Without it, a program that wants its work near its memory has nothing to
place against.
Solution: Adopt flyology_numa as a nested standalone crate that reports it
The crate reads the host description and reports memory nodes, the
processors attached to each, firmware-declared distances, per-node memory,
and processor packages. It does not place memory; that is a separate
question this crate does not answer.
Three decisions shape the interface.
Topology queries are total. A host with no memory-node structure reports one
node holding every processor, because such a host genuinely has one memory
domain. Support distinguishes a description that was read from the host from
that single-domain description. Individual facts a host does not answer use
the existing variant-record convention with Value_Or, so a caller names its
own fallback rather than receiving a plausible number.
Online and permitted nodes are reported separately. A container is shown the
whole machine's node list while a control group restricts what it may
allocate on, so the host description alone does not establish permission.
Allowed_Nodes intersects the description with Mems_allowed_list from the
process status file.
Node and processor numbers are the host's own, and sparse. They match sysfs
paths and numactl output, so a reported number is the number to look up
elsewhere. To_CPU performs the Ada processor mapping GNAT applies unchecked,
and declines when the host's numbering has a gap the mapping cannot carry.
A description this crate cannot read is reported as a shortfall rather than
guessed at. Support.Complete goes false when any part of the description was
refused: a node or processor number beyond the represented range, a list
naming more ranges than the reader holds, an unreadable processor list, or a
permitted-node list the host named and the reader did not understand. The
distinction that matters is between a node with no processor attached, which
is an ordinary memory expander, and a node whose processor list could not be
read, which is not known to have none. A description that names nodes but
attaches no processor to any of them is refused outright, because every
machine has a processor.
The sysfs reader names the directories it reads instead of assuming them, so
it is host-independent text processing that compiles and runs everywhere.
This departs from flyology_cachelines, which keeps all Linux code under a
Linux source directory and tests only the host it runs on. The interesting
cases cannot be reached that way: a development machine has one node and a
container on one is single-node too. Twelve recorded descriptions under
tests/fixtures cover two packages of one node each, two packages each
divided into two memory nodes, a node carrying memory with no processor
attached, sparse node numbering, a control-group restriction, a gap in
processor numbering, an older host naming only its ordinary memory, and five
descriptions the reader must refuse or report as incomplete. All are
exercised on macOS, which has none of them.
Set algebra lives in its own unit rather than the root body because
discovery runs during the root package's elaboration and cannot call back
into the body it is elaborating. Each step of the discovery chain names the
body it needs with pragma Elaborate, so the ordering does not rest on the
binder inferring it. Elaborate_All would not serve: its closure reaches back
through the platform spec to the root body itself.
A processor list is bounded at one range per processor rather than at 64,
because a host that enumerates processors round-robin across its nodes gives
each node a list of single-processor ranges.
Verified with ./flyology_numa/scripts/test.sh on macOS aarch64 from a clean
tree: both the host suite and the fixture suite pass, and clean builds of
the linux, macos, and unsupported platform bodies through the crate's
host-selection scenario variable emit no warnings, style diagnostics, or
errors.
376d2481The crate describes which memory nodes a host has and which processors are
attached to each. A program that wants its work near its memory can read
that and then do nothing with it: there is no way to ask that a region's
pages be drawn from a chosen node.
Solution: Add Flyology_NUMA.Placement over the kernel's memory-policy calls
Placement is a separate package from the reporting one because the two fail
differently. Reading a description always succeeds; acting on one often does
not. Support reports which of three states applies before anything is
attempted, and every operation reports its own outcome rather than raising.
The third state is the one worth having. A host may have no placement
interface, or it may have one this process is refused. A container sandbox
commonly refuses these calls while the machine underneath has memory nodes
that other processes are using, and reporting that as a host without the
facility would be wrong. Denied is therefore distinct from Unsupported_Host.
The two are told apart by what the host said about itself rather than by the
error number alone, because a sandbox may answer a refused call by claiming
the call does not exist: a host that described its own memory nodes has the
calls that act on them, so a refusal from such a host is a denial.
Support is settled on the first call that needs it rather than while the
package elaborates. A sandbox that answers a refused call by ending the
process would otherwise end any program that merely links this crate, even
one that never places memory.
Apply_To places a range, Apply_To_Thread places what the calling thread
allocates from then on, Node_Of_Address reports where a page ended up, and
Permitted_Nodes asks the host afresh rather than reusing what it said
earlier. A range must begin on a page boundary because placement acts on
whole pages; an unaligned range is refused before the host is asked.
Three details of the kernel interface are easy to get wrong and silent when
got wrong, so each is handled deliberately.
The node-bit count is decremented by the kernel before the mask is read, so
passing the number of bits meant would drop the highest node. Placement
passes one more than that, which names every node this crate represents
while still asking for only the eight bytes the mask has. Reading the
permitted nodes is bounded differently again: the kernel refuses a count
below the number of nodes the host could ever have, counting nodes that are
merely possible rather than online, so that read uses a buffer covering the
largest node count Linux can be built for.
Two policies take no node set, and the kernel rejects either of them when
one arrives. Passing the set a caller naturally has to hand would therefore
fail, so the set is dropped before the host sees it and the interface's
promise that it is ignored is kept.
The preferred policy takes a single node, not a set. The interface documents
that only the lowest-numbered node is preferred rather than implying the
whole set contributes.
No C library wraps these calls. Their manual pages document a separate
library's wrappers, not the one every program already links, so they are
reached through the generic system-call entry point. That entry point is
variadic, which is one of the reasons this repository admits C, but GNAT's
variadic convention reaches it correctly without a C file and keeps the
crate free of one, as flyology_cachelines is.
Call numbers differ per architecture, and not only in value: the x86-64
table and the table newer architectures share disagree on the order of
get_mempolicy and set_mempolicy. Calling by a number from the wrong table
reaches a working but unrelated kernel service, which is worse than not
calling at all, so each table is written out separately and an architecture
whose numbers are not recorded disables placement rather than borrowing
them.
That failure is silent by nature, so the host suite has an invariant that
exposes it: a host that described its own memory nodes has the calls that
act on them, so reporting no way to place memory means the calls were not
reached. Building for one architecture with another's table was checked to
fail on exactly that assertion rather than passing quietly, and passing a
node set with a policy that takes none was checked to fail before the set
was dropped.
Verified on macOS aarch64, x86-64 Linux, and AArch64 Linux. macOS reports
Unsupported_Host and places nothing. Both Linux architectures report
Supported and complete every operation: every policy applied to a range and
to the calling thread, a written page reported on an online node, an
unaligned range refused, and an empty node set refused for a policy that
needs one. Builds of the macos, linux and unsupported platform bodies across
x86-64, AArch64 and an architecture with no recorded table emit no warnings,
style diagnostics, or errors.
41e2d423Flyology_NUMA.Placement acts on an address range. Using it to put an object
on a chosen node means obtaining page-aligned memory, placing it, carving
objects out of it, and releasing it afterwards, all in the caller's code and
all outside the language's own way of saying where memory comes from.
Ada already has that way: the storage pool of an access type. Nothing here
supplied one.
Solution: Add Flyology_NUMA.Pools, a storage pool whose subpools are nodes
An access type names the pool and each allocation names the node, so where
an object lives is written at the point it is created rather than arranged
around it:
Item : constant Sample_Access := new (On_Node (Arena, Near)) Sample'(...);
Subpools are the part of the language that fits this. A pool alone would fix
the node when the access type was declared; a subpool is chosen per
allocation, which is what naming a node needs to be.
The pool obtains whole pages from the host rather than taking memory from
the ordinary heap. Heap memory may begin partway into a page whose remainder
belongs to something else, and placing that page would move memory this
crate does not own. Pages are placed before any of them is handed out, since
placing them afterwards would leave whatever had already been written where
the host first put it.
Memory is handed out by advancing through pages already obtained, and goes
back to the host when a subpool is deallocated or the pool is finalized.
Freeing a single object does nothing. That suits memory built up and
discarded together, which is what a node-bound arena usually is, and the
interface says so rather than implying a general allocator.
Ownership of the pages is left to the runtime's own teardown rather than
taken over. The runtime keeps the records it needs to finalize an object
inside the very pages that object lives in, and a pool that gave its pages
back first would take those records with them; a pool holding anything with
finalization would then fail on the way out of its own scope. Deallocating
one subpool likewise forgets it, because the runtime has already detached it
by then and a pool that still believed it owned it could never serve that
node again.
Only the policies that name a node can be given to a pool. The two that name
none place nothing on the node the subpool stands for, so a pool built on
one would report memory placed there that never was.
Placement_Reached separates a host that placed the memory from one that
could not, and says which of the two it means: the host accepted the
request. Under a bound policy that puts the pages on the node; under a
preferred one the host stays free to draw from elsewhere. Allocation
succeeds either way, on the reasoning that memory in the wrong place still
runs while no memory does not.
A pool is not task safe, and the interface says so. Two tasks allocating
from one subpool at once can be handed the same address.
Verified on macOS aarch64, x86-64 Linux, and AArch64 Linux, and inside
guests built to have several memory nodes, which no machine to hand does.
scripts/multinode-check.sh boots a two-node guest and a four-node guest
whose distances are uneven and one of whose nodes carries memory with no
processor attached, then runs the host suite inside them. It was run on
both x86-64 and AArch64, each emulating its own architecture, so that the
placement calls a guest makes are the ones its architecture's table names. Those runs are
what exercise choosing between nodes at all: memory bound to each node in
turn and confirmed held by that node, including the node with no processor;
interleaving spread over every node rather than drawn from one; and a node
shown further from another than from itself.
The suite gained cases for the two failures described above, and each was
confirmed to fail before the change and pass after. Builds of the macos,
linux and unsupported platform bodies across x86-64, AArch64 and an
architecture with no recorded placement table emit no warnings, style
diagnostics, or errors.
a416d8dc./scripts/test.sh runs each crate's own entry point by path, so the file
has to carry the executable bit. flyology_numa/scripts/test.sh was recorded
without it and the whole suite stopped there, reporting only that a command
could not be run.
Running the script as an argument to a shell, which is what a person does
while writing one, works either way and hides this.
Solution: Record the executable bit, as the other crates' entry points do
550bc612The crate ships, and nothing outside its own directory says so. It has no
guide page, no generated API reference, and no entry in the library ledger
or the machine-readable index. A reader who finds the other standalone
crates on the site does not find this one.
Solution: Adopt the crate into the documentation and the site
The crate now builds its own GNATdoc reference, and the root documentation
run builds and asserts it alongside the other crates. Publishing it required
documenting the pool primitives the runtime calls, which had no comments,
and describing the pool's discriminants in prose: @field names a record
component, and a discriminant of a private type is neither.
The package renaming that shortened System.Storage_Pools.Subpools in the
public part is now in the private part. GNATdoc reported the public renaming
on every documentation run, and the full name appears twice in the visible
declarations rather than in every generated page.
The guide page follows the order a reader needs: add the crate, read the
node structure, use the permitted nodes rather than the online ones, handle
a host with one memory domain, place memory, allocate from a node, and read
distances and packages for what they say rather than what they suggest.
Every API link on that page was resolved through the generated search index
rather than by reading the generated markup, which does not put an entity's
name where a reader of the HTML would expect it.
The library ledger keeps its alphabetical order, so the new entry is S05 and
the two entries after it move up.
Verified with ./scripts/docs.sh, which builds the reference with no
undocumented-entity warnings, and ./scripts/build-site.sh followed by the
site checker, which reports 144 HTML files and 201 total files. The crate's
own suite still passes.
ae1e3ddfThe project map in the hero names each standalone library, and the
catalogue below it links each one. Both lists stop at the crates that
existed before this one. A reader who arrives at the site and looks for what
Flyology offers does not learn that the crate exists.
Solution: Add the crate to the hero project map and the catalogue
Both lists keep their alphabetical order, so the entry sits between IRI and
SIMD. The catalogue entry links to the guide, as the entries for the other
crates with a guide on this site do.
Verified with ./scripts/build-site.sh and the site checker, and by fetching
the built pages: the hero and catalogue both list the crate, and all 39
internal links on its guide page resolve.
aa8b12edThe page opens with a short definition in a callout and then goes straight
to adding the crate. A reader who does not already know what a memory node
is learns the interface before the reason for it, and a reader who does know
still gets no picture of the machine the crate describes.
Solution: Open with a section that explains memory nodes and draws one
The section states what changes when a machine has more than one processor
package, draws a two-node machine with a local and a remote access, and says
plainly when placement is worth doing at all: a program that fits in cache,
or one that allocates little, does not gain from it. The callout it replaces
said less.
The diagram uses only the classes the figure context defines. An earlier
draft used a class that context does not style, and SVG then fell back to a
solid black fill. That reads as deliberate against a dark background and
wrong against a light one, which is how it survived a first look.
Both themes were checked by computed style rather than by eye. The
containers take the page's own surface and border colours, so they follow
the theme instead of fighting it.
Verified with ./scripts/build-site.sh and the site checker.
7b70cbf6Buddy, best-fit, and TLSF live inside the main package hierarchy, so bare-board runtimes cannot reuse them without also accepting Flyology identity, mapping, and shared-memory concerns.
Solution: Extract allocation algorithms into a standalone crate
Add flyology_allocators 0.1.0-dev with caller-owned regions, timed contention retries, native and ARM bare-board verification, and optimized memory primitives. Keep magic and schema policy in a thin Flyology adapter, pin the new crate from Flyology, and update integration tests and documentation.
7bf86246The extracted algorithms lack a reproducible benchmark against the platform allocator, making hot-path regressions and algorithm tradeoffs difficult to measure.
Solution: Add a dedicated flyology_bench allocator shootout
Add an optional hosted benchmark subcrate with fixed-size and fragmented-churn workloads comparing malloc, Buddy, Best-Fit, and TLSF in balanced paired rounds. Keep benchmark dependencies outside the bare-board crate closure and document release and smoke workflows.
be44ac54Profiling shows recursive Buddy allocation and release spend most of their time rebuilding and validating structure-relative node addresses. Release also validates each handle twice before its first mutation, making small allocation cycles substantially slower than the other standalone algorithms.
Solution: Cache the validated Buddy node-table binding
Retain the process-local node-table base in the attached view after validating its complete native range, use bounded node-relative addressing on hot paths, clear the binding on detach, and remove the redundant release validation while preserving validation before mutation.
283d5e71Profiles of fixed allocation cycles show TLSF spending most of its time in free-list insertion and removal, block initialization, and handle validation. Each logical operation repeatedly recomputes and validates the same block base before touching adjacent scalar fields, leaving the constant-time algorithm dominated by address machinery.
Solution: Reuse validated block bases across TLSF hot paths
Validate each referenced block extent once, carry its process-local base through list mutation, splitting, coalescing, and handle release, and keep stored link, size, generation, state, and bitmap checks intact. Avoid a redundant ready-state check while the arena guard is held and replace size-class division by its exact power-of-two shift.
e9199d66Profiling shows fixed-layout address validation, duplicate split and coalesce writes, and repeated AVL balance reads dominate Best-Fit allocation and release. This leaves both fixed cycles and fragmented churn substantially slower than necessary.
Solution: Streamline checked Best-Fit metadata operations
Cache the last complete block-header location in the local view, use a fixed-field address path that retains attachment and complete-header bounds checks, avoid tree metadata writes that insertion immediately replaces, and compute AVL balance inputs once per ancestor. The stored layout, persisted validation, guard behavior, handle generations, and bare-board dependency boundary remain unchanged.
05d2eb83The benchmark project selects release switches only for its own sources. Imported flyology_allocators units are compiled without optimization, making comparisons against the optimized system malloc misleading and overstating allocator hot-path costs.
Solution: Force a clean optimized benchmark closure
Build the benchmark and all imported Ada sources through gprbuild with explicit O3, debug symbols, Ada 2022, warnings, and validity checks. Document why the runner must apply switches across project boundaries.
77e1d163GNAT reports parenthesized array aggregates as obsolescent when the benchmark closure is compiled as Ada 2022, obscuring useful build diagnostics.
Solution: Adopt Ada 2022 bracketed array aggregates
Convert Flyology Bench array initializers and expressions to bracket syntax while retaining parentheses for record aggregates. Keep the obsolescent warning category enabled so future occurrences remain visible.
77ce4c04Release repeatedly discovers physical neighbors and removes and reinserts free-list entries even when the next allocation can reuse the same size class. This makes steady fixed-size and churn workloads pay fragmentation-recovery costs on every operation.
Solution: Defer TLSF coalescing until allocation requires it
Return released blocks directly to their size class and perform a bounded, checked physical coalescing pass only after a normal TLSF search misses or before destroying an empty arena. Preserve boundary tags and bitmap/list validation, and cover adjacent runs, allocated gaps, attachment, stale handles, and complete teardown with adversarial tests.
562bdd61Lazy coalescing makes worst-case allocation linear and no longer joins neighbors during release, but both capability records and package comments retain the eager algorithm contract. Flyology also reuses the eager persisted identity even though version 2 readers reject valid adjacent free blocks produced by the new implementation.
Solution: Publish the lazy TLSF contract and identity
Report the linear worst-case search and deferred release coalescing in standalone and Flyology packages, document the miss-triggered pass, and advance Flyology TLSF to version/schema 3 so cross-version attachment fails at the immutable header. Add standalone capability coverage and a Flyology regression that rejects the complete eager identity.
288b03a2Profiling shows fixed-size allocation cycles spend most of their Best-Fit time splitting a large free block during allocation and immediately removing and coalescing its remainder during release. Fragmented churn repeats related AVL maintenance even when the same physical block can be reused directly.
Solution: Defer Best-Fit coalescing until allocation needs it
Index released blocks directly for exact-size reuse. Before reporting exhaustion, validate the complete physical chain, coalesce every adjacent free run, repair boundary links, and retry the AVL search. Accept the resulting persisted representation on attachment, canonicalize empty arenas during destruction, report the resulting linear worst-case search capability, and cover adjacent frees, alternating gaps, stale handles, attachment validation, and teardown.
41578832Deferred coalescing leaves adjacent free blocks as a valid persisted Best-Fit state. The earlier eager reader rejects that state, but Flyology still advertises the same outer version and schema, so compatibility fails only after entering nested allocator validation.
Solution: Version the Flyology Best-Fit lazy invariant
Advance the Flyology-only adapter version and schema while retaining its magic. This makes older readers reject lazy images at the outer identity boundary and keeps standalone allocator metadata free of application identity.
ec813587The lazy Best-Fit adapter advances its persisted version and schema, but the Flyology data-structure smoke test neither proves rejection of the eager version-2 identity nor reflects the linear fallback and deferred-coalescing capabilities.
Solution: Exercise the Best-Fit version-3 boundary
Temporarily replace a live Best-Fit arena outer identity with the version-2 values and require attachment to fail closed, then restore the image. Assert the truthful lazy-coalescing capabilities for both Best-Fit and TLSF.
e1315591Small allocation cycles repeatedly collapse a released leaf to the root and split the same path on the next allocation. Profiling shows those release stores and recursive rebuilding dominate Buddy's fixed-size cost.
Solution: Coalesce Buddy trees only when allocation needs it
Keep released nodes in the retained split tree, compact free subtrees before a search reports genuine exhaustion, and compact before empty-arena destruction. Add adversarial coverage for half-tree and root reconstruction plus stale handles, and expose the lazy-coalescing capability accurately.
4ad89a24After deferring coalescing, profiling shows fixed-size Buddy allocation is dominated by walking the same split path. Fragmented churn also revisits released orders even though the releasing view already knows a suitable free node.
Solution: Cache one validated free node per Buddy order
Keep process-local node hints in each attached view, validate persisted state before every hinted reuse, and discard all hints during coalescing and detach. Preserve the persisted layout and fall back to the complete tree for stale or missing hints; cover stale hints across independently attached views.
22998f64Buddy still advertises logarithmic eager-coalescing behavior, an eager Flyology reader can attach a retained all-free split tree and falsely report exhaustion for a parent-sized request, and Destroy may release its guard after a partially failed coalescing pass without poisoning the image.
Solution: Harden lazy Buddy capabilities and lifecycle handling
Advertise linear miss search and deferred coalescing, advance only Flyology's outer Buddy identity to version 3, and verify the retained two-leaf compatibility case. Check that an arena is empty before Destroy mutates it and poison any coalescing failure. Compile the standalone project as Ada 2022 for bracket aggregates and extend standalone and Flyology compatibility tests.
66888189Buddy, Best-Fit, and TLSF now defer coalescing until an allocation miss, but the root, standalone, and website guides still advertise their former fast-path and worst-case behavior. The stale prose also describes a process-shared guard even though the standalone algorithms are usable with arbitrary caller-owned storage.
Solution: Document lazy coalescing and view-scoped state
Describe each successful lookup and miss-triggered coalescing path, report the resulting linear worst-case bound, and use attached-view terminology. Record that stored geometry remains stable while the valid state contract changes and Flyology adapters use version-3 identities.
94cd5b57Buddy, Best-Fit, and TLSF now retain free structure on release and coalesce on allocation misses. Behavioral tests cover examples, but no bounded state-machine review checks shared guard ownership, block partitions, index publication, stale view hints, generation-stamped release, false exhaustion, or operation completion across interleavings.
Solution: Model guarded allocator transitions in TLA+
Add a parameterized extraction for the three algorithms with policy-specific splitting and selection, TLSF bin and bitmap state, Buddy view-local hints, lazy coalescing, retry, generations, and fairness. Add safe TLC configurations plus required false-exhaustion, stale-hint, stale-bitmap, stale-release, and nontermination counterexamples, wire them into the authoritative runner, and document the production mapping and abstraction limits.
4dc3e5c0The bounded Buddy, Best-Fit, and TLSF models establish safety and liveness for their extraction, but review alone does not detect when persisted metadata, view-local hints, generations, handles, or size-class computations diverge from the standalone kernels.
Solution: Compare canonical Ada and TLC allocator traces
Add test-only private-state observers and bounded public-operation traces for all three kernels. Model the same traces in TLA+, compare every operation-boundary snapshot byte-for-byte in the authoritative runner, require no-retry liveness counterexamples, and run the conformance boundary in CI while retaining standalone ARM cross-build isolation.
54d77f0fSolution: add a standalone bitmap-slab and contiguous-span allocator, bridge it into Flyology, benchmark it against the existing policies and malloc, and cover its bounded state machine with positive and deliberately broken TLA+ configurations.
239aa004Solution: publish the affected 32-bit bitmap word with release ordering and acquire it before validating the slot generation. Add bounded safe and deliberately broken TLA+ visibility models so TLC reproduces the former stale-handle acceptance.
195b83bfSolution: add a corruption test that makes one large-span head claim its neighboring live allocation, then validate every head and tail descriptor field before reporting capacity or allowing release to mutate the claimed extent.
7a7bc16fThe hero project map, the standalone catalogue, the library ledger, and
llms.txt each name every standalone crate. This branch adds a new one, and
all four lists stop at the crates that existed when the branch was written.
A reader who arrives at the site and looks for what Flyology offers does not
learn that the crate exists.
Solution: Add the crate to the hero map, catalogue, ledger, and llms.txt
Every list keeps its alphabetical order, so the entry sits first among the
standalone crates and the ledger indices shift from S01..S07 to S02..S08.
The crate publishes neither a guide page on this site nor a generated API,
because scripts/docs.sh does not build one and flyology.gpr excludes the
crate from documentation. The entries therefore link to the source tree
only, as the Flyology QUIC entry already does for the same reason. Adding an
api/flyology_allocators/ link would fail the site checker.
Verified with ./scripts/build-site.sh and the site checker, which passes on
141 HTML files, and by grepping the built pages: the hero, catalogue,
ledger, and llms.txt all name the crate.
7dc419f2This branch turns Allocation_Algorithms.Buddy, Best_Fit, and TLSF from
documented packages into generic instantiations of Allocation_Algorithms
.Contract. GNATdoc emits no unit page and no declaration anchor for an
instantiation, so the three API links the guide carries for them now resolve
to nothing. main gained a site link checker after this branch was written,
so the Pages workflow reports all three and fails.
Solution: Present the three instantiations as code rather than links
AGENTS.md asks that the first explanatory mention of a public entity link to
its verified generated GNATdoc unit or declaration entry. No entry is
generated for these three, so there is nothing to link, and pointing them at
the kernels or at Contract would name a different entity than the prose
does. The surrounding Arenas, Contract, Allocation_Capabilities, and View
links are unaffected and still resolve.
Verified with ./scripts/build-site.sh and the site checker, which now passes
on 141 HTML files and 198 total files with zero problems.
9a24bf68This branch adds Allocation_Algorithms.Slab_Span as a fourth arena allocator
and says so in the README, but the guide section that tells a reader how to
choose one still presents three. It names three adapters, describes three
policies, gives three decision rows, and twice states that all three share
one metadata guard. A reader choosing an allocator from this page does not
learn the fourth exists, and the two counts now contradict the README.
Solution: Describe slab/span alongside the other three and correct the counts
The introductory paragraph names the adapter and states its shape: bitmap
slots in fixed runs for small requests, contiguous runs for larger ones, with
descriptors and generation tables outside the managed bytes. A decision row
gives the selection rule and the reclamation behavior a caller can observe:
requests up to half the configured run size take a generation-stamped slot,
larger requests reserve whole runs, release keeps an emptied slab indexed for
reuse, and a miss reclaims empty slabs to obtain the run or span it needs.
The row also states that the run size is an allocator quantum rather than an
operating-system page, because the name invites the opposite assumption.
The half-run threshold matches the kernel, which sends a request to a slab
class when Requested <= Run_Size / 2 and to a span otherwise. Both "all
three" counts become four. The adapter is a generic instantiation, so like
Buddy, Best_Fit, and TLSF it has no generated GNATdoc entry to link.
Verified with ./scripts/build-site.sh and the site checker, which passes on
141 HTML files and 198 total files, and by parsing the built page: tags
balance and the new row renders between TLSF and the compile-time choice.
9a24bf68 → updated to 60de2c69index 8a8e9de51 source commit
60de2c69flyology.gpr imports flyology_allocators.gpr, and the manifest both declares
flyology_allocators as a dependency and pins it to the in-repo directory.
Alire honours pins only in a root crate's manifest, so a consumer resolving
flyology from the index never receives that dependency. Its solution omits
flyology_allocators entirely and gprbuild then fails with "imported project
file flyology_allocators.gpr not found". A crate whose only dependency is
flyology = "~0.1.1-dev" reproduces it in two lines.
Nothing in this repository reports the fault, because the pin satisfies the
dependency for every build performed here. The pin also hid two further
assumptions that only the in-repo directory could satisfy.
Solution: Resolve the allocators dependency from the index
Drop the pin so the declared dependency resolves the same way for this crate
and for its consumers. The published flyology_allocators 0.1.0-dev is built
from this repository at the same commit and the same subdirectory, so the
source reached is identical; only the path by which it is reached changes.
Configure the organization index in this repository's own workflows. They
carried the community index alone, because the pin meant no job here ever
resolved flyology_allocators from an index, and it is published only in the
organization index. Without that step alr reports an incomplete solution
naming flyology_allocators as missing.
Name the excluded documentation project through the imported project rather
than through a repository path. Documentation.Excluded_Project_Files listed
flyology_allocators/flyology_allocators.gpr, which resolved only while the
crate sat in this working tree; as a dependency its project lives in the
Alire build tree under a content hash. Flyology_Allocators'Project_Dir asks
the imported project where it is, so the exclusion holds wherever the
dependency is deployed and the allocator packages stay out of this crate's
generated API, where they would otherwise be documented twice.
Record the rule that produced the fault in the agent guide: no committed
pins in a crate manifest, alr pin in the consuming workspace for unpublished
work, and a declared dependency must resolve from the index. Name the
gprbuild error there too, since that is where the fault appears and it names
a project file rather than a crate.
60de2c69 → updated to 9782c81bindex 75a836be16 source commits
aeb10612The hero project map, the standalone catalogue, the library ledger, and
llms.txt each name every component. Flyology RDF publishes a website, a
guide, and three generated API references at rdf.flyology.org, but the name
appears nowhere in this repository. A reader who arrives at the site and
looks for what Flyology offers does not learn that the project exists.
Solution: Add the project to the hero map, catalogue, ledger, and llms.txt
flyology_rdf depends on flyology_iri only. It needs no runtime preparation
and starts no tasks, so it belongs with the standalone libraries. Every list
keeps its alphabetical order, so the entry sits between Flyology NUMA and
Flyology SIMD and the ledger indices shift from S07..S08 to S08..S09.
The ledger and llms.txt entries carry all three generated references, as the
Flyology Postgres entries already do, because the project site treats the
Notation3 and SPARQL APIs as first-class navigation.
The llms.txt project-structure summary also omitted Flyology Allocators and
Flyology NUMA, which the same file already listed further down. Both names
join the summary here.
Verified with the website-kit site checker, whose output is unchanged from
the pre-change baseline, and by rendering the pages: the hero, catalogue,
and ledger all name the project. The six linked URLs return 200.
2188ee0dThe figure caption claimed "One project, two adoption paths" above two cards
that already carry the same fact in their labels, so the caption restated
what the diagram shows. The two card subtitles were not parallel either:
"Core component" named a role while "No runtime dependency" named a
dependency, so neither pair member helped a reader compare the paths.
The component names below them were plain list items. A reader who found the
name that matched the problem had no way to reach the component from the
diagram and had to scroll to the catalogue to find the same name again.
Solution: Drop the caption, make the subtitles parallel, and link the names
The subtitles now read "Prepare the runtime" and "Add a crate", which state
the two adoption paths as the actions they are. The figure keeps an
accessible name through aria-label, and the figcaption rule leaves the
stylesheet with the element it styled.
Each component name becomes a link to its best available destination, which
is the destination the catalogue below already uses: the project site where
one exists, then the guide, then the generated API, then the source tree.
The pill moves from the list item to the anchor so the whole badge is the
hit target. Links open in a new tab and carry no underline, as requested, so
hover brightens the border and adds a faint fill instead. Each list states
the new-tab behavior once in its aria-label rather than repeating it in
twelve hidden spans.
Verified by rendering the page: every badge computes text-decoration-line
none and cursor pointer, and the hovered badge is distinguishable from its
neighbors. The site checker reports one new line against the baseline,
api/flyology_debug/ from the new Debug badge, which resolves once
scripts/docs.sh generates the GNATdoc output that this partial build omits.
5dfd5d36Two measurement runs that share a machine produce two wrong answers, and
neither run can tell that it happened. The problem is not confined to
benchmarks: a load generator, a soak test, or a profiler makes the machine
loud, and a benchmark needs it quiet. Both want the same thing from opposite
directions, and nothing lets them find each other.
Worse, two runs that each suspend collection when they detect interference
are symmetric controllers observing each other. Each pauses because the other
is loading the machine, both then observe quiet, both resume together, and
both immediately pause again.
Solution: Specify a host CPU lock convention and implement it
The convention is named for the resource rather than for benchmarking, so
that the tools which create load take the same claim as the tools which need
its absence. Conflict detection is flock and nothing else: liveness is the
lock itself, so a holder that dies releases with no cleanup path, no stale
registry entries, and no pid-reuse hazard.
A machine-wide claim takes LOCK_EX on the claim file; a claim on individual
CPUs takes LOCK_SH there and LOCK_EX on one file per CPU. Disjoint core-scoped
claims therefore proceed concurrently while a machine-wide claim excludes them
all, with no registry and no arbiter. Per-CPU claims are acquired in ascending
order, because a total order is what stops two callers with overlapping sets
from deadlocking on each other's partial claims.
The path is a single deterministic default rather than a writability-probing
fallback chain. A chain reintroduces the failure it was meant to prevent:
/run/lock is 1777 on Debian and root-owned 0755 on Red Hat derivatives, so a
root process and an unprivileged process on one machine would probe
differently, land on different files, and each conclude it holds the machine.
$TMPDIR is avoided for the same reason, being per-user on macOS.
File content is diagnostic and is never consulted to decide conflicts. It is
still specified and parseable, one key=value pair per line, because a reader
must be able to name the holder; cwd is included because tool and pid alone do
not distinguish one checkout or CI workspace from another. Readers open the
file without locking, since a shared lock would block behind the very holder
they are trying to identify, and must tolerate content that is empty, stale,
or half-written.
A claim is not proof of exclusivity, and the implementation does not present
it as one. systemd PrivateTmp= bind-mounts a per-service directory over /tmp,
so two services share every CPU and cannot see each other's claim files; that
case is detected from /proc/self/mountinfo and reported as namespace-scoped.
Separate containers are not detectable from inside and need an explicit shared
path. A path can be proven private but never proven machine-wide.
Only the C macros live in the native unit. GNAT's C_Variadic_2 convention
calls open() correctly even on Darwin/AArch64, where variadic arguments are
passed differently from fixed ones, so the protocol, the file grammar, and the
/proc reader are all Ada. The convention compiles on every GNAT this crate
supports, checked against 13.2.2 through 16.1.0 against an alire.toml floor of
13, so no compatibility shim is carried.
Verified by building the crate, and by taking claims on disk: the file matches
the published grammar, parses with awk -F=, is created 0666, and per-CPU files
are named beside the machine file.
13b71a5bCPU_Quiescence samples host counters once, before clock characterization, and
then never looks again. A machine that goes busy halfway through collection
produces a result that reads as clean, and nothing in the measurement, the
console card, or the machine-readable output says otherwise. The gate's own
documentation admits it "cannot prevent new load from arriving after
collection begins", and until now that was the end of the story.
The obvious repair is not available either. Host utilization cannot simply be
compared against a limit during collection the way the preflight does it,
because by then the benchmark thread saturates a core and every sample would
trip the limit.
Solution: Watch for foreign load during collection and say what was found
Foreign load is host busy time minus this process's own CPU time, so load the
benchmark itself creates is correctly not foreign. The estimate scales the
host busy ratio by wall time rather than converting ticks, so the platform
tick length never enters the arithmetic. Cross-checked against top on a loaded
machine, the two agree to within 0.1 percent.
Three responses form a ladder. Observe records and keeps everything. Retake
discards the contaminated window and collects it again. Pause waits for the
host to settle, re-warms, and then collects it again. The re-warm is not
politeness: a resumed run has cold caches, predictors, and frequency state, so
without it the first sample after a pause is the outlier the pause existed to
avoid.
Nothing adjusts a reported statistic. A skew factor derived from observed load
would be invented data, because the cost of interference depends on whether
the contention is an SMT sibling, a shared cache, memory bandwidth, or another
socket, and the same foreign share can mean no slowdown or a large one. Runs
are annotated or repaired, never corrected.
Observation happens between timed samples over windows of whole collection
units, where a unit is one sample, one comparison pair, or one balanced
multi-way round. A response therefore never splits the two halves of a pair
and never lands mid-round, where it would spread interference unevenly across
the cases a round exists to compare fairly. Windows carry a margin over the
configured minimum: sized to only just reach it, ordinary batch jitter drops
them below it and they silently degrade to observation. Host counters are
tick-based, so a window that still falls short is recorded but never acted on.
Discarding a window rolls back what the window changed. Per-sample values are
written by index and survive a retake intact, but the position tally of a
comparison pair and the process-telemetry summaries are running sums, so a
discarded pass gives them back. Without that, a run with eighteen retained
samples and twenty-four retakes would report elapsed time and an average CPU
share covering forty-two samples, most of which were thrown away.
Budgets degrade rather than abort. An exhausted retake or pause budget
continues under Observe and reports it, and paused time is excluded from
Maximum_Sampling_Time because waiting for the host is not collection.
Placement is optional and sharpens attribution: with a strict binding every
other CPU's busy time is foreign outright, and the placed CPU's share is its
busy time minus the placed thread's own. SMT siblings join the watched set,
because a process saturating the sibling of the placed CPU slows the
measurement while leaving the placed CPU's own share clean, which makes
sibling-blind core-scoped observation worse than host-wide observation rather
than better. Placement is Linux-only in practice; Apple Silicon implements no
thread affinity and rejects every request, so the Host_Control documentation
no longer describes Darwin placement as merely advisory.
Placement binds only the calling thread, so another thread of this process can
occupy a watched CPU, where its time is indistinguishable from foreign load.
Each window therefore reads process CPU as well as thread CPU, and their
difference bounds how much of the watched capacity those threads could account
for. Once that bound exceeds the configured foreign limit, the core-scoped
answer can no longer address the question the limit asks: the run drops to
host-wide observation, which is well defined for a multi-threaded process, and
reports Attribution_Diluted. Otherwise a scheduler benchmark run under
placement would spend its retake budget discarding samples over load it
created itself. The window that reveals the dilution is discarded rather than
recorded, because its value was computed under the attribution being
abandoned.
The direction of that fallback is deliberate. Core-scoped attribution
subtracts only the placed thread, which over-reports interference when other
threads share the watched CPUs; subtracting the whole process instead would
deduct time our threads spent on CPUs outside the watched set, under-report
foreign load, and let contaminated data pass as clean. Over-reporting shows up
in the retake and contamination counts; under-reporting shows up nowhere.
The host CPU claim is taken before the preflight gate, since waiting for a
quiet host and then blocking on the claim leaves the quiet verdict stale.
Everything observed reaches the console card, the CSV schema, and the JSON
object. That visibility is the point: a run that had to repair itself must say
so, or a heavily repaired result reads as a quieter machine than the one it
ran on.
Verified with ./scripts/test.sh on Darwin/aarch64 and in an Ubuntu 24.04
container on Linux/aarch64, and under qemu-system-aarch64 across four CPU
topologies. Core-scoped attribution asserts that it closed an observation
window, so the thread-CPU probe it depends on cannot fail silently. Two
assertions were checked against builds that defeat them: one with sibling
collection disabled fails the sibling assertion under cores=2,threads=2 and
passes without SMT, and one with the dilution check disabled fails the
fallback assertion while a task of the test's own process burns CPU. Retake
and pause are driven by a spawned process, because load raised inside this
process is its own CPU time and is subtracted by design.
845bdacawebsite/AGENTS.md governs hand-written pages under guide/**. The interference
section met none of its API-link requirements and several of its style rules.
Every public entity it introduced went unlinked: Interference, Placement,
Host_Lock, the three Interference_Response literals, Maximum_Retakes, Window,
Rewarm_Time, Maximum_Foreign_CPU_Percent, Include_Siblings, Require_Strict,
Attribution_Diluted, and Environment. The same page already links
CPU_Quiescence, Stable_Time, and Timeout, so the new section also read
inconsistently against its own neighbours.
Solution: Link the API entities and apply the three prescribed reviews
The three reviews ran in sequence and are labelled here, since separate review
agents were unavailable.
Technical. "Foreign load is host busy time minus this process's own CPU time"
held only without placement, because core-scoped attribution subtracts the
placed thread rather than the whole process; the sentence now says so, and the
placement paragraph states which CPUs the harness measures. "The re-warm is
required" overstated a configurable field, so the text names Rewarm_Time and
says what a zero value leaves behind. "When the difference exceeds the
configured limit" named neither the limit nor the compared quantity, which
also left Maximum_Foreign_CPU_Percent unlinked. Two harnesses that both pause
do not deadlock as claimed; they alternate between pausing and resuming.
Editorial. "This matters most" and "Without it" both referred back to
something unnamed. Both now name their subject.
Controlled language. "Sharpens attribution" was figurative and is now
"narrows attribution to the placed CPU and its SMT siblings". "A run that
repaired itself reports that fact" personified the run; the report is the
actor, and it states counts.
Verified with ./scripts/build-site.sh and the site checker, which passes 144
HTML files. All fourteen API links resolve to an existing anchor in the
generated GNATdoc output, and each target matches the entry the API search
index gives for that entity. No sentence in the section exceeds the 25-word
review signal.
edf788efThe interference section described three capabilities without saying that a
run acquires none of them unless it asks. BENCH 07 states that contract for
the preflight gate, and the page marks the default value of a choice where one
exists, as it does for Equal_Time and Balanced_Rounds. The new section did
neither, so a reader could not tell that enabling the watch alone changes
nothing about the collected data.
The claim file was also absent. The page named three environment variables but
not the one path that other tools must agree on, leaving the coordination
surface a click away in the convention document.
Solution: State the defaults, the claim path, and the budget interaction
Observe is now marked as the default response, in the form the page already
uses. The closing paragraph mirrors BENCH 07: the watch, placement, and the
host claim are disabled by default, so an existing run acquires no new wait or
failure condition. The opening sentence calls the watch optional where the
reader first meets it.
The claim paragraph names /tmp/host-cpu.lock, the Path field that overrides it
for one run, and the HOST_CPU_LOCK_PATH variable that overrides it for every
tool following the convention. An earlier draft named only the variable, which
implied it was the sole override; the technical review caught that against
Resolved_Path and Try_Acquire.
The Window row gains the limit that qualifies it. A window covers whole units,
so a window spanning many short samples can exceed Maximum_Retakes on its
first discard. Collection then continues under Observe and reports the
exhausted budget, which otherwise looks like the response never ran.
Numeric field defaults are deliberately not repeated here. No other section of
the page tabulates them, every field now links to its generated entry, and a
duplicated value is one that can go stale.
Verified with ./scripts/build-site.sh and the site checker, which passes 144
HTML files. All fifteen API links resolve to an existing anchor, and each
target matches the entry the API search index gives for that entity, including
the ambiguous Path simple name, which resolves to Host_Lock_Policy.Path. No
sentence in the section exceeds the 25-word review signal, and no entity is
linked twice.
4fbe5020recording_smoke drives two benchmarks from four tasks and asserts that the
slow benchmark's recorded median wall time exceeds the fast one's. The
separation comes from delay 0.000_2 against delay 0.000_8. A relative delay
is rounded up to the host timer tick, and on a Linux kernel with a coarse
tick both requests wake at the same boundary near 3 ms. A four-task probe
with no recorder involved shows the first iteration of each task honoring
the request and every later one landing on the same plateau:
0.0002 s: 323 3710 2829 2972 3069 2947 ... (us)
0.0008 s: 1167 3307 2482 3625 2793 3045 ... (us)
The two recorded medians then arrive at 2.98 ms and 2.99 ms, ordered by
scheduling noise alone. On Ubuntu 24.04 with GNAT 16.1.0 on aarch64 the
assertion fails about half the time: 9 of 20 runs on a clean checkout. macOS
resolves the two delays apart, which is why the test has held there. The
independent comparison checked two assertions below rests on the same
separation and fails with it.
Solution: Separate the distributions by work the workers perform
Each worker spins a volatile rotate-and-xor accumulator, 20_000 rounds for
the fast benchmark and twenty times that for the slow one, the idiom the
file's concurrent PMU-session block already uses. That is roughly 33 us
against 680 us, a twentyfold gap read by a nanosecond wall clock, so no
timer resolution enters the measurement. It also costs less than the
sleeping it replaces: the slow workers take about 14 ms rather than 60 ms.
The assertion stands, since the ordering is the invariant worth holding, but
its message now carries both medians so a failure on an unusual host reports
numbers rather than a verdict.
Verified on the failing environment, an Ubuntu 24.04 container with GNAT
16.1.0 on aarch64: 30 of 30 runs pass where HEAD fails 9 of 20 in the same
container, 20 of 20 under a two-CPU quota, and 10 of 10 with 24 competing
spinners on that quota. scripts/test.sh passes three times from a fresh
source export there and once on macOS. A calibration probe over 105 further
trials across those CPU configurations held the ratio between 14x and 30x
with no inversions and Relative_Change_Percent positive throughout.
ae5b52d7Flyology.Observability.Group_Snapshot carries thirty fields covering queue
depths, wait categories, and cumulative counters, but none of them reports how
much of an event loop's time was spent doing work. Pool imbalance is therefore
invisible from outside the process: a group with a deep ready queue and a group
that is idle produce the same evidence once their momentary queue depths match.
Made_Progress detects a stalled loop but not a saturated one, and its comment
already records why a per-dispatch clock read was rejected as the answer.
Solution: Time the poller wait a loop takes when nothing is ready
An event loop enters Wait_Batch only when its ready queue is empty, so the idle
path is both the exact complement of the loop's working time and a path a busy
loop never repeats. The scheduler stamps the wait before releasing the group
lock and closes it after reacquiring, keeping the in-progress wait in the group
so an observer adds it rather than charging a long block as busy time. Nothing
is added to dispatch, context switch, or any other hot path.
Group_Snapshot gains Uptime_Nanoseconds, Idle_Nanoseconds, and Idle_Waits, and
the runtime snapshot ABI moves to version 6. Idle time is clamped to uptime and
both come from one clock reading, so callers can always take the difference or
the ratio. tests/native/process_lifecycle_probe.c pins that ABI and its layout,
and moves with it.
The cost is measured rather than asserted. runtime_callback_bench adds
poller_idle_cycle for the instrumented path and monotonic_clock_read for the
unit of work it adds; benchmarks/idle_wait_rate reports the rate a loop
actually reaches, because the effect is far below what a differential timing
run resolves on this host. On macOS/AArch64 a reading costs about 20 ns, a
socket ping-pong reaches roughly 32,000 to 38,000 waits per second per loop,
and a loop doing nothing else reaches roughly 120,000 to 160,000: under 0.2%
and under 0.7% of a loop's time respectively.
Checks run: ./scripts/test.sh (pass, including the new
observability_utilization_smoke), ./scripts/docs.sh (pass, no new warnings),
./scripts/bench-runtime.sh.
6c4bfc3cStep 10 of the guide lists the readings that diagnose a group, but every one of
them describes queue contents. A reader who wants to know whether an event loop
is saturated has no entry to follow, and the utilization fields the runtime now
publishes appear only in README.md and the generated API reference.
Solution: Document loop utilization where the guide diagnoses a group
A new subsection explains what the runtime measures, why the subtraction is
always valid, and the two readings that look alike: a group whose tasks all
wait resembles a group with nothing to do, and a task that never suspends keeps
its loop out of the poller and reports as fully busy. A decision row places the
measure next to the queue-depth readings it qualifies. The cost paragraph names
the measured figures and the script that reproduces them, rather than asserting
that the accounting is cheap.
The worked example is compiled and run by tests/guide_utilization_example.adb.
The first draft of that example did not compile, because Counter is a modular
subtype and the subtraction needs a use type clause the guide had not shown. A
test main is the only check that keeps a printed example honest about the
visibility a reader must supply.
Checks run: ./scripts/test.sh (pass, including the new
guide_utilization_example), ./scripts/build-site.sh, and check-site.mjs over
the built site as CI runs it.
fb881640The website/AGENTS.md technical review is required for any changed capability,
limit, or timing claim, and running it on the new guide section found four
faults.
Two are wrong rather than unclear. The guide told a reader that `Counter` needs
a `use type` clause because the subtype is modular; the clause is needed
because the operators of a type declared in another package are not directly
visible, so the stated rule teaches the wrong thing. The guide also said an
event loop waits in its poller only when no task can run, but the loop polls
with a zero timeout while work is ready, and the same section counts that
polling as busy time.
The remaining two narrow a claim that had become broader than the code.
"Dispatching, polling with work ready, or running task code" reads as an
exhaustive account of a loop's busy time and omits timer promotion, file
submission, and cancellation processing. That wording appeared in the guide,
README.md, and the Group_Snapshot field documentation, so all three change
together. `Group_Snapshot` and `Snapshot` appeared only inside the code block
and were linked nowhere in prose, which the API-link rule does not allow.
Solution: Correct the two faults and narrow the two broad claims
The editorial and controlled-language passes contributed three further changes:
one repeated paragraph opening, "idle fraction" used before the section
introduces it, and "reading" carrying both a clock read and a snapshot.
Checks run: ./scripts/docs.sh (pass, no new warnings),
./scripts/build-site.sh with check-site.mjs over the built site,
and observability_utilization_smoke, guide_utilization_example, and
observability_smoke rebuilt and re-run.
bca8ad7fscripts/build-site.sh writes the complete site to build/site, but nothing
records how to serve it. Reading a guide page as it will be published means
inventing a static-server invocation each time, and picking a port that does
not collide with a development server already running.
Solution: Record a launch configuration that serves the built site
The entry serves build/site on port 8231. It reads the build output and does
not produce it, so run scripts/build-site.sh first and again after editing
anything under website/.
Checks run: served build/site through the entry and loaded the guide page.
104d406fThe macOS body queries the flat hw.l1dcachesize sysctl. On a heterogeneous
Apple silicon host that name reports the efficiency-core capacity, not the
performance-core capacity: one 12P/4E host reports 65536 bytes for the flat
name against 131072 for hw.perflevel0.l1dcachesize. Flyology_Cachelines.
L1_Data_Cache_Size therefore reports half the performance-core L1d capacity,
and L1_Data_Cache_Slots derives its slot count from that halved value.
The reader also assumed every queried name stores a 64-bit value. It read
each name into one 64-bit object and accepted a short reply, so a 32-bit
value was correct only because Darwin is little-endian and the object was
zero-initialized. The per-level names are 32-bit.
Solution: Prefer the highest performance level and read the reported width
macOS 12 and later describe a host whose cores are not identical as ordered
performance levels, where hw.perflevel0 is the highest-performing level. The
macOS body now queries hw.perflevel0.l1dcachesize and falls back to the flat
name only on a host that publishes no performance level, such as macOS 11 and
earlier or an Intel Mac. Querying hw.nperflevels first would add nothing: the
per-level name exists exactly when the host publishes performance levels.
Hardware_Cache_Line_Size is unchanged, because Darwin publishes no per-level
line size and both Apple silicon core types use the same line.
The public API keeps its Cache_Query_Result surface. Reporting per-level
geometry is a separate design question, and it should not ride along in a
detection correctness fix.
The sysctl reader moves into a new private Flyology_Cachelines.Macos child,
mirroring the existing Flyology_Cachelines.Linux split between mechanism and
policy. It requests each name's stored width before reading the value and
rejects any other width, so a 32-bit name, a 64-bit name, a string, and an
absent name are all handled without depending on byte order. Failed and
malformed queries still degrade to Unavailable rather than raising.
The GNATdoc comments on L1_Data_Cache_Size and L1_Data_Cache_Slots, the crate
README, and the website cachelines guide now state that a host whose cores are
not identical reports one core type, and which one each platform reports. A new
macOS detection test mirrors the Linux one: it exercises the sysctl reader
directly and checks that the reported capacity is the highest performance level
the host publishes.
Checks run: ./flyology_cachelines/scripts/test.sh (the native test now reports
131072 bytes of L1 data cache on a 12P/4E host, and the new macOS detection
test passes) and ./flyology_cachelines/scripts/docs.sh (zero GNATdoc warnings).
d3af8647Flyology_Cachelines.Linux_Testing asserts that both Detect_From_Sysconf and
Detect_From_Sysfs report a value. Only their composition is guaranteed:
glibc answers _SC_LEVEL1_DCACHE_SIZE and _SC_LEVEL1_DCACHE_LINESIZE from
CPUID on x86-64, but reports zero on aarch64, where sysfs is the only
mechanism. Detect already treats sysconf as optional and falls back to
sysfs, so the test is stricter than the code it covers.
The test therefore fails on aarch64 Linux with "sysconf cache detection
failed". CI runs ubuntu-latest on x86-64, where sysconf answers, so the
failure is invisible there. It reproduces on aarch64 Linux, which AGENTS.md
tells contributors to prefer on Apple silicon because it is native and
faster.
Solution: Require the composition and check each available result
Run now requires that at least one mechanism reports a value, and checks
consistency only for a mechanism that reported one. That is what the
production detector promises: it composes sysconf and sysfs, and returns
Unavailable only when neither answers.
Checks run: the crate suite on aarch64 Linux in a container, where the
detection test now passes, and on macOS, where it is not built.
04658ed7Flyology_Cachelines.Linux reads one L1 data-cache geometry: sysconf answers
for the calling CPU, and the sysfs fallback reads cpu0. A host whose cores
are not identical has more than one geometry, and neither mechanism says
which core the reported one belongs to, or that others exist.
macOS already publishes ordered performance levels, so the macOS body can
prefer the highest-performing one. Linux publishes per-CPU descriptions and
no single ordering, so the equivalent information has to be derived before
any public per-core-type query can work on both platforms.
Solution: Group CPUs into core classes and record what ordered them
Detect_Core_Classes enumerates the CPUs the host lists as present, reads
each one's L1 data-cache geometry and capacity, groups CPUs reporting
identical values into a class, and counts each class.
Classes are grouped on capacity and geometry together. Grouping on geometry
alone merges two core types that happen to share an L1 data-cache size,
which real designs do.
Ordering uses whichever key separates the classes, and Core_Classes reports
which one did, so a caller can tell an authoritative order from an inferred
one:
- Host_Reported: the kernel's per-CPU capacity values separated them.
Capacity is the scheduler's own measure of relative CPU performance and
accounts for microarchitecture and clock. Architectures selecting the
generic topology code publish it under cpuN/cpu_capacity.
- Inferred: no capacity separated them, so they are ordered by descending
L1 data-cache capacity. That holds on current hybrid parts and carries
no meaning on a host whose classes differ some other way. x86-64 does
not publish capacity, so its hybrid parts land here.
- Unordered: fewer than two classes, or nothing separated them.
Cores and CPUs are counted separately. SMT siblings share one L1 data cache,
so the cache's sharing list identifies which CPU represents a physical core.
Counting logical CPUs as cores overstates a 24-core host with two threads
per core by a factor of two.
Detection fails closed. A missing or malformed CPU list leaves only CPU 0 to
enumerate, enumeration is bounded, a CPU whose description cannot be read is
skipped, and a host reporting more classes than the table holds reports none
rather than a table that silently omits a class.
Detect_Core_Classes takes the CPU root as a parameter defaulted to the real
sysfs path. Grouping, ordering, and the SMT distinction cannot be reached on
a uniform test host, so the detection test drives them with committed
fixture trees: two classes separated only by capacity, two classes separated
only by geometry with the smaller listed first, SMT pairs sharing one cache,
and a CPU list with a gap.
Nothing calls Detect_Core_Classes outside the test yet. The public
per-core-type query, and the elaboration cost of walking every CPU, belong
to the change that adds that query.
Checks run: the crate suite on aarch64 Linux in a container and on macOS.
Detection was also replayed against captured CPU descriptions from a 48-CPU
x86-64 host with two threads per core, which reports 24 cores against 48
CPUs and no capacity, and from an 80-CPU aarch64 host, which reports 80
cores against 80 CPUs and a published capacity.
ba8438daFlyology_Cachelines reports one L1 data-cache capacity. A host that combines
core types has one per type, so a single value silently describes whichever
type the platform body happened to pick and gives a caller no way to ask
about the others, or to learn that others exist.
The two platforms also disagreed. macOS reported the highest performance
level, while Linux reported whichever cache its own mechanism described:
sysconf for the calling CPU, sysfs for cpu0. The same call therefore meant
different things on the two platforms.
Solution: Make the core class an explicit parameter of every cache query
Core_Class names one class of cores that report a single cache geometry.
Core_Class_Count reports how many the host distinguishes, Core_Class_Cores
and Core_Class_CPUs describe each one, and L1_Data_Cache_Size and
L1_Data_Cache_Slots take the class they describe. The parameter defaults to
Fastest_Core_Class, so existing calls keep compiling and now mean the same
thing on both platforms: the class the host ranks highest.
Core_Class_Ordering reports what produced that rank, because it is a host
fact on some platforms and the crate's inference on others. macOS performance
levels and the Linux per-CPU capacity values are Host_Reported. A host
publishing no rank is Inferred, ordered by descending L1 data-cache capacity.
Fewer than two classes is Unordered.
Cores and CPUs are reported separately. Sibling CPUs under simultaneous
multithreading share one core's L1 data cache, so a 24-core host with two
threads per core reports 24 cores and 48 CPUs in one class.
Hardware_Cache_Line_Size takes no class. macOS publishes no per-class line
size, and the core types of current heterogeneous parts share one line size,
so a per-class query would have one answer to give.
The host is now inspected on first use rather than during elaboration.
Describing every class means reading one description per CPU on Linux, which
measures 14 ms for 48 CPUs and 24 ms for 80. A program that links this crate
and never asks about the host should not pay that at startup. The inspection
is idempotent, so a race on the first query can duplicate work but cannot
produce a value neither caller computed; the ready flag is atomic and is set
after the result is stored.
Flyology_Cachelines.Platform now answers one Detect call describing the whole
host, and the root package body owns the single memoization. The two Linux
bodies share one translation and differ only in what they report when sysfs
describes nothing: the x86-64 body can still read the calling CPU through
CPUID. Flyology_Cachelines.Linux drops its own class and ordering types in
favour of the public ones, so the crate has one Core_Class.
The website cachelines guide is corrected alongside the API. Adding the Class
parameter changed the generated GNATdoc anchors for L1_Data_Cache_Size and
L1_Data_Cache_Slots, so the guide's links to them became dangling fragments;
both now point at the regenerated anchors. The guide also claimed detection
happens during package elaboration, which is no longer true. The new entities
are linked to their generated entries on first mention.
Checks run: the crate suite on macOS, where a 12P/4E host reports two
host-ordered classes of 131072 and 65536 bytes across 12 and 4 cores; on
aarch64 Linux in a container; and on an x86-64 Linux host with 24 cores and
two threads per core, which exercises the CPUID body and reports 49152 bytes
across 24 cores and 48 CPUs. GNATdoc builds with no warnings.
9782c81bFlyology_Cachelines reports L1 but not L2, so a caller sizing a working set
against the level it will actually spill into has nothing to read.
The flat sysctl this crate would naturally have used is wrong in the same way
hw.l1dcachesize was, and by a larger factor. On a 12P/4E Apple silicon host
hw.l2cachesize reports 4194304, the efficiency cluster's cache, against the
performance cluster's 16777216. Adding L2 before core classes existed would
have reproduced the original defect at four times the size instead of two.
Reporting a capacity alone would also mislead. L2 is rarely private: that same
host shares 16 MiB across six performance cores and 4 MiB across four
efficiency cores, so neither figure is a per-core budget.
Solution: Report L2 capacity and how many cores share it, per class
L2_Cache_Size and L2_Sharing_Cores take the same core class as the L1
queries. A sharing count of one means the cache is private to a core; a
larger count means the reported capacity is divided among that many cores.
The GNATdoc on each function points at the other, because reading either
alone invites the per-core mistake.
macOS reads hw.perflevelN.l2cachesize and hw.perflevelN.cpusperl2. Linux
reads the level 2 entry of each CPU's cache description, counting the CPUs
its shared_cpu_list names. Both counts arrive as logical CPUs and become core
counts through the class's own threads-per-core ratio, so a host with two
threads per core does not report twice the cores sharing a cache.
Parsing a CPU list needs more than its leading index, so the Linux body gains
List_Count, which sums the single indexes and inclusive ranges a list names.
Describing L2 costs no extra file reads: the level 2 entry is found in the
same pass over the cache indexes that already locates the level 1 data entry.
The website cachelines guide gains the same pairing, with both names linked
to their generated entries.
Checks run: the crate suite on all three supported targets. A 12P/4E macOS
host reports 16777216 bytes across 6 cores for its performance class and
4194304 across 4 for its efficiency class, and the macOS test asserts that
these come from the performance level rather than the flat name. An aarch64
Linux host reports 16777216 across 6 cores. An x86-64 Linux host with 24
cores and two threads per core reports 2097152 across 1 core, where sysfs
gives 2048K shared by CPUs "0,24"; that run also compiles the CPUID body,
which builds nowhere else. Fixtures cover a per-class shared L2, an L2 shared
across SMT siblings, and a private L2. GNATdoc builds with no warnings.
9782c81b → updated to 7be049fcindex 6e2db0ef1 source commit
7be049fcThe two OpenSSL install steps in the test job carried no bound, so a mirror
that stops answering leaves apt waiting rather than returning an error. On
2026-08-19 run 32297071503, job 96211136305 sat on "Install OpenSSL 3 on
Linux" for 32 minutes and had to be cancelled by hand; it was the only check
blocking PR #49. Its log shows apt-get update fetching noble-security
InRelease at 20:13:04 and producing no further output until cancellation. The
macOS half of the same job passed, so nothing was wrong with the tests.
Left alone the job would have run to GitHub's six-hour default. Nothing else
in either workflow was bounded either: the checkout, the Alire install, the
index configuration, the Java and TLA+ downloads, and the GNATdoc install all
reach the network under the same default, and no job in this repository
declared a timeout at all, unlike the sibling repositories.
Solution: Bound every job and every step that reaches the network
Each job now declares a timeout-minutes, and so does each step that fetches
something. The bounds are generous multiples of what healthy runs measure
over the last 60 successful runs of each workflow, so they can only fire on a
stall. Jobs: test observes at most 17 minutes on macOS and 11 on Linux and is
bounded at 60; proof observes 6 minutes and TLA+ 7, both bounded at 30; the
pages build observes 69 seconds and is bounded at 20, its deploy 10 seconds
and bounded at 15. Steps: everything network-touching is bounded at 10
minutes against observed maxima of 1 to 6 seconds, except the Alire install,
which downloads the toolchain, observes 100 seconds, and is bounded at 15.
A step timeout alone would only have converted the 32-minute hang into a
10-minute one, because the stall is inside a single apt-get update that never
returns. That command is now capped per attempt with timeout(1) and retried
twice with 15 and 30 second backoff, so one unresponsive mirror costs a
retry rather than the job. The install that follows is capped as well but not
retried: a package that cannot be resolved should fail on the first try. The
worst case is three 120-second attempts plus a 10-second kill grace, 45
seconds of backoff, and a 190-second install, which is 10m25s and lands
inside the 12-minute step bound, so the annotation naming the transient
mirror is what the log ends on rather than a bare cancellation.
GNU timeout is used directly rather than scripts/run-with-timeout.sh. That
helper exists for portability to macOS, and this step is Linux-only.
The TLA+ download gains --connect-timeout 30 and --max-time 120 alongside its
existing --retry 3, because curl retries a failed transfer but not a stalled
one, and --max-time resets per attempt. Four bounded attempts stay inside the
step's 10 minutes.
Build, test, proof, and model-checking steps keep no step bound. Their
duration is workload-dependent, a bound sized for them would be a performance
budget rather than a stall detector, and the job timeout already covers them.
Nothing about what is installed, or how anything is built or tested, changes.
Checks run: both workflows parse. The retry script was extracted from the
committed YAML and driven under bash -e against stub apt-get invocations for
five outcomes: first-try success runs one update and one install; two
failures then success runs three updates and installs; three failures exit 1
with the annotation and never install; an install failure propagates its exit
status without retrying; and exit 124, which is what timeout(1) reports when
it kills a stalled command, is treated as retryable. The real commands were
then run in an ubuntu:24.04 container on coreutils 9.4, the runner's version:
the bounded update succeeds, interrupting one mid-flight reports 124, a
retry after that kill still succeeds rather than inheriting a poisoned apt
state, and the bounded install still yields OpenSSL 3.0.13 through pkg-config.
The convention is recorded in AGENTS.md so it holds for new steps, and it
matches the job-level timeouts already used in flyology-http and
gnat-patches, whose install steps want the same treatment.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7be049fc → updated to a53da35dindex b55699621 source commit
a53da35dThe allocator crate is developed alongside the runtime in this repository and
released on the same development line, but the dependency on it is declared as
"*". Nothing stops a dependent from resolving an allocator release built for a
different line, and the index has been carrying a tighter constraint by hand to
compensate.
Solution: Constrain allocators to its development line
Declare the line the allocator crate actually publishes, so the constraint
travels with the manifest instead of being reapplied wherever it is indexed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.1.1-devCache-line-aware storage and cache information for Ada
91d6a0f6 → updated to 9a24bf68index 510cfcf639 source commits
40849230The functional loopback checks use retry intervals too narrow for occasional macOS hosted-runner stalls. An early client exception also waits behind cancellation coordination and loses the operation that failed, leaving only a generic lifecycle assertion.
Solution: tolerate stalls and report DNS smoke stages
Use functional smoke deadlines that accommodate runner scheduling, let coordination observe early client completion, and report the execution model and operation stage for unexpected exceptions. The dedicated DNS resilience smoke retains narrow deadline coverage.
347604c6Session logs are distributed across project checkouts, worktrees, sister
repositories, and nested agent sessions. Their token counters also contain
cached input, repeated cumulative records, inherited history, and duplicated
Claude message entries, making straightforward aggregation inaccurate.
Solution: add a portable incremental AI usage audit
Add a self-contained PEP 723 Python script that discovers the workspace from
its containing repository and scans associated Codex and Claude Code logs,
including worktrees, subagents, and workflows.
Deduplicate cumulative and inherited records, categorize base input, cache
reads and writes, output and reasoning tokens, and estimate API-style costs
using model-specific pricing. Persist JSON cursors and daily aggregates so
subsequent runs process only new log records.
Provide summary, daily, weekly, provider, and model breakdowns with JSON
output, date filtering, configurable paths and timezone, state rebuilding,
and integrity diagnostics.
73925055The landing page, navigation, guide index, and machine-readable documentation center Flyology Runtime while standalone libraries share the project identity without depending on it. Visitors cannot reliably distinguish the runtime core, runtime-dependent protocol libraries, and independently adoptable components, and several component documentation paths are missing or placed under the wrong section.
Solution: Present Flyology as the project and Runtime as its core
Reorganize the landing page around the project umbrella, add focused Runtime and Libraries pages, move Guide, Architecture, and the main API under Runtime, separate standalone documentation from the Runtime guide, link each published component Guide and API, fix the scrollable guide topic rail, and align README, product, design, journal, build assertions, and llms.txt with the same dependency model.
6cb6887fThe redesigned project pages repeat the experimental label in several nearby locations while the curated Libraries directory has no path to the complete package index. Readers see redundant maturity warnings but cannot easily reach version, dependency, availability, and compiler-build records for every published package.
Solution: Clarify maturity guidance and link the complete crate index
Keep experimental status in the primary explanatory context, make constraints and adoption guidance more direct, and link crates.flyology.org from the Libraries introduction, footer, and llms.txt documentation map.
4738d656Only the Libraries page exposes the complete package catalogue in its footer. Readers arriving on Runtime, Guide, Architecture, Journal, or the project overview have no equivalent utility path to package versions, dependencies, and availability records.
Solution: Add the crate index to every shared footer
Place the Crate index utility link first in each hand-written footer, including the journal entry template, while retaining every page-specific navigation link.
5b7c3925Check_Parent_Cancellation_Is_Prompt in tests/task_scope_faults_smoke requires
every one of 16 rounds to propagate parent cancellation within 5 ms.
Propagation crosses two lightweight-task wakes, and a contended shared runner
can deschedule either of them for milliseconds, so the check reports host
scheduling as often as it reports the mechanism. CI run 32100324387 failed
this assertion on its first attempt and passed on a rerun of the identical
commit 71df9ed, whose only change is .github/workflows/pages.yml. Locally,
96 spinning processes on 16 cores plus nice -n 20 reproduce the failure in
2 of 30 runs, and 1280 rounds measured under that load put the median at
78 us with 0.9 per cent of rounds past 5 ms: the mechanism is prompt and the
bound is reporting the host. A bare pragma Assert also names only a source
line, so a real regression is indistinguishable from a stalled round.
Solution: Require most rounds to be prompt, bound the rest, and report them
The check now measures all 16 rounds, requires at least 10 of them within
1 ms, and requires no round past a 0.5 s sanity ceiling. That is a stronger
claim about the mechanism rather than a weaker one. The retired 10 ms polling
monitor spreads its latency across its period and places about 1.6 of 16
rounds inside 1 ms, so it clears the new bar about once in two million runs
against once in sixty-five thousand for the old whole-round 5 ms bound;
reinstating that monitor put 0 of 16 rounds inside 1 ms and failed the new
check on all five idle runs. Under the load that failed the old check in
11 per cent of runs, the new check passed 60 of 60. A failing round set is
printed to standard error so the next failure names the rounds and their
latencies instead of only the assertion line.
857820c4Problem: Cancellation promptness is asserted as a per-round wall-clock bound
5dfa0d25The site check that validates local links and fragment targets runs only in
the Pages workflow, which triggers on a push to main and on manual dispatch.
No pull-request workflow builds the site, and ci.yml has no site job, so a
change that breaks a link passes every check a reviewer sees, merges, and
then fails the Pages build on main. The site stops publishing at the point
where the mistake is hardest to attribute.
The hand-written pages carry 457 links into generated GNATdoc entries across
eleven guide and architecture pages. GNATdoc names each entry by a hash of
the declaration, so changing a public subprogram's parameters gives it a new
anchor and silently strands every hand-written link to it. Editing the
surrounding documentation comment does not, which makes the failure easy to
miss: prose edits are safe, and a signature change quietly is not.
Solution: Build and check the site on pull requests
The Pages workflow now also runs on pull_request. The build job is
unchanged for a push, and on a pull request it builds the site and runs the
same link check without touching Pages: configuring Pages, uploading the
artifact, and the deploy job are all skipped. A pull request therefore needs
no Pages permission, which also keeps the job usable from a fork.
Concurrency now groups per ref. One group for every event serialized
unrelated pull requests behind each other and behind a deploy. Pushes to main
still share one group and are still never cancelled; a pull request cancels
its own superseded runs.
This adds a toolchain and GNATdoc build to each pull request, which is the
cost of resolving generated anchors: the check can only confirm a fragment
exists by generating the page that defines it.
Checks run: the workflow parses, has no tabs, guards exactly the three
Pages-specific steps, and keeps the link check unguarded. check-site.mjs was
confirmed to catch this class of breakage against a page carrying one stale
API fragment, where it reports "missing fragment target" and exits non-zero.
This pull request triggers the new job, so its own run is the end-to-end
check.
252f4fd7A machine with more than one processor package attaches memory to each of
them, and memory attached to the package a thread runs on is reached faster
than memory attached to another. Nothing in this repository reports that
structure. Ada itself does not describe it either: System.Multiprocessors
covers processors and dispatching, and the language has no notion of a
memory node at all.
Without it, a program that wants its work near its memory has nothing to
place against.
Solution: Adopt flyology_numa as a nested standalone crate that reports it
The crate reads the host description and reports memory nodes, the
processors attached to each, firmware-declared distances, per-node memory,
and processor packages. It does not place memory; that is a separate
question this crate does not answer.
Three decisions shape the interface.
Topology queries are total. A host with no memory-node structure reports one
node holding every processor, because such a host genuinely has one memory
domain. Support distinguishes a description that was read from the host from
that single-domain description. Individual facts a host does not answer use
the existing variant-record convention with Value_Or, so a caller names its
own fallback rather than receiving a plausible number.
Online and permitted nodes are reported separately. A container is shown the
whole machine's node list while a control group restricts what it may
allocate on, so the host description alone does not establish permission.
Allowed_Nodes intersects the description with Mems_allowed_list from the
process status file.
Node and processor numbers are the host's own, and sparse. They match sysfs
paths and numactl output, so a reported number is the number to look up
elsewhere. To_CPU performs the Ada processor mapping GNAT applies unchecked,
and declines when the host's numbering has a gap the mapping cannot carry.
A description this crate cannot read is reported as a shortfall rather than
guessed at. Support.Complete goes false when any part of the description was
refused: a node or processor number beyond the represented range, a list
naming more ranges than the reader holds, an unreadable processor list, or a
permitted-node list the host named and the reader did not understand. The
distinction that matters is between a node with no processor attached, which
is an ordinary memory expander, and a node whose processor list could not be
read, which is not known to have none. A description that names nodes but
attaches no processor to any of them is refused outright, because every
machine has a processor.
The sysfs reader names the directories it reads instead of assuming them, so
it is host-independent text processing that compiles and runs everywhere.
This departs from flyology_cachelines, which keeps all Linux code under a
Linux source directory and tests only the host it runs on. The interesting
cases cannot be reached that way: a development machine has one node and a
container on one is single-node too. Twelve recorded descriptions under
tests/fixtures cover two packages of one node each, two packages each
divided into two memory nodes, a node carrying memory with no processor
attached, sparse node numbering, a control-group restriction, a gap in
processor numbering, an older host naming only its ordinary memory, and five
descriptions the reader must refuse or report as incomplete. All are
exercised on macOS, which has none of them.
Set algebra lives in its own unit rather than the root body because
discovery runs during the root package's elaboration and cannot call back
into the body it is elaborating. Each step of the discovery chain names the
body it needs with pragma Elaborate, so the ordering does not rest on the
binder inferring it. Elaborate_All would not serve: its closure reaches back
through the platform spec to the root body itself.
A processor list is bounded at one range per processor rather than at 64,
because a host that enumerates processors round-robin across its nodes gives
each node a list of single-processor ranges.
Verified with ./flyology_numa/scripts/test.sh on macOS aarch64 from a clean
tree: both the host suite and the fixture suite pass, and clean builds of
the linux, macos, and unsupported platform bodies through the crate's
host-selection scenario variable emit no warnings, style diagnostics, or
errors.
376d2481The crate describes which memory nodes a host has and which processors are
attached to each. A program that wants its work near its memory can read
that and then do nothing with it: there is no way to ask that a region's
pages be drawn from a chosen node.
Solution: Add Flyology_NUMA.Placement over the kernel's memory-policy calls
Placement is a separate package from the reporting one because the two fail
differently. Reading a description always succeeds; acting on one often does
not. Support reports which of three states applies before anything is
attempted, and every operation reports its own outcome rather than raising.
The third state is the one worth having. A host may have no placement
interface, or it may have one this process is refused. A container sandbox
commonly refuses these calls while the machine underneath has memory nodes
that other processes are using, and reporting that as a host without the
facility would be wrong. Denied is therefore distinct from Unsupported_Host.
The two are told apart by what the host said about itself rather than by the
error number alone, because a sandbox may answer a refused call by claiming
the call does not exist: a host that described its own memory nodes has the
calls that act on them, so a refusal from such a host is a denial.
Support is settled on the first call that needs it rather than while the
package elaborates. A sandbox that answers a refused call by ending the
process would otherwise end any program that merely links this crate, even
one that never places memory.
Apply_To places a range, Apply_To_Thread places what the calling thread
allocates from then on, Node_Of_Address reports where a page ended up, and
Permitted_Nodes asks the host afresh rather than reusing what it said
earlier. A range must begin on a page boundary because placement acts on
whole pages; an unaligned range is refused before the host is asked.
Three details of the kernel interface are easy to get wrong and silent when
got wrong, so each is handled deliberately.
The node-bit count is decremented by the kernel before the mask is read, so
passing the number of bits meant would drop the highest node. Placement
passes one more than that, which names every node this crate represents
while still asking for only the eight bytes the mask has. Reading the
permitted nodes is bounded differently again: the kernel refuses a count
below the number of nodes the host could ever have, counting nodes that are
merely possible rather than online, so that read uses a buffer covering the
largest node count Linux can be built for.
Two policies take no node set, and the kernel rejects either of them when
one arrives. Passing the set a caller naturally has to hand would therefore
fail, so the set is dropped before the host sees it and the interface's
promise that it is ignored is kept.
The preferred policy takes a single node, not a set. The interface documents
that only the lowest-numbered node is preferred rather than implying the
whole set contributes.
No C library wraps these calls. Their manual pages document a separate
library's wrappers, not the one every program already links, so they are
reached through the generic system-call entry point. That entry point is
variadic, which is one of the reasons this repository admits C, but GNAT's
variadic convention reaches it correctly without a C file and keeps the
crate free of one, as flyology_cachelines is.
Call numbers differ per architecture, and not only in value: the x86-64
table and the table newer architectures share disagree on the order of
get_mempolicy and set_mempolicy. Calling by a number from the wrong table
reaches a working but unrelated kernel service, which is worse than not
calling at all, so each table is written out separately and an architecture
whose numbers are not recorded disables placement rather than borrowing
them.
That failure is silent by nature, so the host suite has an invariant that
exposes it: a host that described its own memory nodes has the calls that
act on them, so reporting no way to place memory means the calls were not
reached. Building for one architecture with another's table was checked to
fail on exactly that assertion rather than passing quietly, and passing a
node set with a policy that takes none was checked to fail before the set
was dropped.
Verified on macOS aarch64, x86-64 Linux, and AArch64 Linux. macOS reports
Unsupported_Host and places nothing. Both Linux architectures report
Supported and complete every operation: every policy applied to a range and
to the calling thread, a written page reported on an online node, an
unaligned range refused, and an empty node set refused for a policy that
needs one. Builds of the macos, linux and unsupported platform bodies across
x86-64, AArch64 and an architecture with no recorded table emit no warnings,
style diagnostics, or errors.
41e2d423Flyology_NUMA.Placement acts on an address range. Using it to put an object
on a chosen node means obtaining page-aligned memory, placing it, carving
objects out of it, and releasing it afterwards, all in the caller's code and
all outside the language's own way of saying where memory comes from.
Ada already has that way: the storage pool of an access type. Nothing here
supplied one.
Solution: Add Flyology_NUMA.Pools, a storage pool whose subpools are nodes
An access type names the pool and each allocation names the node, so where
an object lives is written at the point it is created rather than arranged
around it:
Item : constant Sample_Access := new (On_Node (Arena, Near)) Sample'(...);
Subpools are the part of the language that fits this. A pool alone would fix
the node when the access type was declared; a subpool is chosen per
allocation, which is what naming a node needs to be.
The pool obtains whole pages from the host rather than taking memory from
the ordinary heap. Heap memory may begin partway into a page whose remainder
belongs to something else, and placing that page would move memory this
crate does not own. Pages are placed before any of them is handed out, since
placing them afterwards would leave whatever had already been written where
the host first put it.
Memory is handed out by advancing through pages already obtained, and goes
back to the host when a subpool is deallocated or the pool is finalized.
Freeing a single object does nothing. That suits memory built up and
discarded together, which is what a node-bound arena usually is, and the
interface says so rather than implying a general allocator.
Ownership of the pages is left to the runtime's own teardown rather than
taken over. The runtime keeps the records it needs to finalize an object
inside the very pages that object lives in, and a pool that gave its pages
back first would take those records with them; a pool holding anything with
finalization would then fail on the way out of its own scope. Deallocating
one subpool likewise forgets it, because the runtime has already detached it
by then and a pool that still believed it owned it could never serve that
node again.
Only the policies that name a node can be given to a pool. The two that name
none place nothing on the node the subpool stands for, so a pool built on
one would report memory placed there that never was.
Placement_Reached separates a host that placed the memory from one that
could not, and says which of the two it means: the host accepted the
request. Under a bound policy that puts the pages on the node; under a
preferred one the host stays free to draw from elsewhere. Allocation
succeeds either way, on the reasoning that memory in the wrong place still
runs while no memory does not.
A pool is not task safe, and the interface says so. Two tasks allocating
from one subpool at once can be handed the same address.
Verified on macOS aarch64, x86-64 Linux, and AArch64 Linux, and inside
guests built to have several memory nodes, which no machine to hand does.
scripts/multinode-check.sh boots a two-node guest and a four-node guest
whose distances are uneven and one of whose nodes carries memory with no
processor attached, then runs the host suite inside them. It was run on
both x86-64 and AArch64, each emulating its own architecture, so that the
placement calls a guest makes are the ones its architecture's table names. Those runs are
what exercise choosing between nodes at all: memory bound to each node in
turn and confirmed held by that node, including the node with no processor;
interleaving spread over every node rather than drawn from one; and a node
shown further from another than from itself.
The suite gained cases for the two failures described above, and each was
confirmed to fail before the change and pass after. Builds of the macos,
linux and unsupported platform bodies across x86-64, AArch64 and an
architecture with no recorded placement table emit no warnings, style
diagnostics, or errors.
a416d8dc./scripts/test.sh runs each crate's own entry point by path, so the file
has to carry the executable bit. flyology_numa/scripts/test.sh was recorded
without it and the whole suite stopped there, reporting only that a command
could not be run.
Running the script as an argument to a shell, which is what a person does
while writing one, works either way and hides this.
Solution: Record the executable bit, as the other crates' entry points do
550bc612The crate ships, and nothing outside its own directory says so. It has no
guide page, no generated API reference, and no entry in the library ledger
or the machine-readable index. A reader who finds the other standalone
crates on the site does not find this one.
Solution: Adopt the crate into the documentation and the site
The crate now builds its own GNATdoc reference, and the root documentation
run builds and asserts it alongside the other crates. Publishing it required
documenting the pool primitives the runtime calls, which had no comments,
and describing the pool's discriminants in prose: @field names a record
component, and a discriminant of a private type is neither.
The package renaming that shortened System.Storage_Pools.Subpools in the
public part is now in the private part. GNATdoc reported the public renaming
on every documentation run, and the full name appears twice in the visible
declarations rather than in every generated page.
The guide page follows the order a reader needs: add the crate, read the
node structure, use the permitted nodes rather than the online ones, handle
a host with one memory domain, place memory, allocate from a node, and read
distances and packages for what they say rather than what they suggest.
Every API link on that page was resolved through the generated search index
rather than by reading the generated markup, which does not put an entity's
name where a reader of the HTML would expect it.
The library ledger keeps its alphabetical order, so the new entry is S05 and
the two entries after it move up.
Verified with ./scripts/docs.sh, which builds the reference with no
undocumented-entity warnings, and ./scripts/build-site.sh followed by the
site checker, which reports 144 HTML files and 201 total files. The crate's
own suite still passes.
ae1e3ddfThe project map in the hero names each standalone library, and the
catalogue below it links each one. Both lists stop at the crates that
existed before this one. A reader who arrives at the site and looks for what
Flyology offers does not learn that the crate exists.
Solution: Add the crate to the hero project map and the catalogue
Both lists keep their alphabetical order, so the entry sits between IRI and
SIMD. The catalogue entry links to the guide, as the entries for the other
crates with a guide on this site do.
Verified with ./scripts/build-site.sh and the site checker, and by fetching
the built pages: the hero and catalogue both list the crate, and all 39
internal links on its guide page resolve.
aa8b12edThe page opens with a short definition in a callout and then goes straight
to adding the crate. A reader who does not already know what a memory node
is learns the interface before the reason for it, and a reader who does know
still gets no picture of the machine the crate describes.
Solution: Open with a section that explains memory nodes and draws one
The section states what changes when a machine has more than one processor
package, draws a two-node machine with a local and a remote access, and says
plainly when placement is worth doing at all: a program that fits in cache,
or one that allocates little, does not gain from it. The callout it replaces
said less.
The diagram uses only the classes the figure context defines. An earlier
draft used a class that context does not style, and SVG then fell back to a
solid black fill. That reads as deliberate against a dark background and
wrong against a light one, which is how it survived a first look.
Both themes were checked by computed style rather than by eye. The
containers take the page's own surface and border colours, so they follow
the theme instead of fighting it.
Verified with ./scripts/build-site.sh and the site checker.
7b70cbf6Buddy, best-fit, and TLSF live inside the main package hierarchy, so bare-board runtimes cannot reuse them without also accepting Flyology identity, mapping, and shared-memory concerns.
Solution: Extract allocation algorithms into a standalone crate
Add flyology_allocators 0.1.0-dev with caller-owned regions, timed contention retries, native and ARM bare-board verification, and optimized memory primitives. Keep magic and schema policy in a thin Flyology adapter, pin the new crate from Flyology, and update integration tests and documentation.
7bf86246The extracted algorithms lack a reproducible benchmark against the platform allocator, making hot-path regressions and algorithm tradeoffs difficult to measure.
Solution: Add a dedicated flyology_bench allocator shootout
Add an optional hosted benchmark subcrate with fixed-size and fragmented-churn workloads comparing malloc, Buddy, Best-Fit, and TLSF in balanced paired rounds. Keep benchmark dependencies outside the bare-board crate closure and document release and smoke workflows.
be44ac54Profiling shows recursive Buddy allocation and release spend most of their time rebuilding and validating structure-relative node addresses. Release also validates each handle twice before its first mutation, making small allocation cycles substantially slower than the other standalone algorithms.
Solution: Cache the validated Buddy node-table binding
Retain the process-local node-table base in the attached view after validating its complete native range, use bounded node-relative addressing on hot paths, clear the binding on detach, and remove the redundant release validation while preserving validation before mutation.
283d5e71Profiles of fixed allocation cycles show TLSF spending most of its time in free-list insertion and removal, block initialization, and handle validation. Each logical operation repeatedly recomputes and validates the same block base before touching adjacent scalar fields, leaving the constant-time algorithm dominated by address machinery.
Solution: Reuse validated block bases across TLSF hot paths
Validate each referenced block extent once, carry its process-local base through list mutation, splitting, coalescing, and handle release, and keep stored link, size, generation, state, and bitmap checks intact. Avoid a redundant ready-state check while the arena guard is held and replace size-class division by its exact power-of-two shift.
e9199d66Profiling shows fixed-layout address validation, duplicate split and coalesce writes, and repeated AVL balance reads dominate Best-Fit allocation and release. This leaves both fixed cycles and fragmented churn substantially slower than necessary.
Solution: Streamline checked Best-Fit metadata operations
Cache the last complete block-header location in the local view, use a fixed-field address path that retains attachment and complete-header bounds checks, avoid tree metadata writes that insertion immediately replaces, and compute AVL balance inputs once per ancestor. The stored layout, persisted validation, guard behavior, handle generations, and bare-board dependency boundary remain unchanged.
05d2eb83The benchmark project selects release switches only for its own sources. Imported flyology_allocators units are compiled without optimization, making comparisons against the optimized system malloc misleading and overstating allocator hot-path costs.
Solution: Force a clean optimized benchmark closure
Build the benchmark and all imported Ada sources through gprbuild with explicit O3, debug symbols, Ada 2022, warnings, and validity checks. Document why the runner must apply switches across project boundaries.
77e1d163GNAT reports parenthesized array aggregates as obsolescent when the benchmark closure is compiled as Ada 2022, obscuring useful build diagnostics.
Solution: Adopt Ada 2022 bracketed array aggregates
Convert Flyology Bench array initializers and expressions to bracket syntax while retaining parentheses for record aggregates. Keep the obsolescent warning category enabled so future occurrences remain visible.
77ce4c04Release repeatedly discovers physical neighbors and removes and reinserts free-list entries even when the next allocation can reuse the same size class. This makes steady fixed-size and churn workloads pay fragmentation-recovery costs on every operation.
Solution: Defer TLSF coalescing until allocation requires it
Return released blocks directly to their size class and perform a bounded, checked physical coalescing pass only after a normal TLSF search misses or before destroying an empty arena. Preserve boundary tags and bitmap/list validation, and cover adjacent runs, allocated gaps, attachment, stale handles, and complete teardown with adversarial tests.
562bdd61Lazy coalescing makes worst-case allocation linear and no longer joins neighbors during release, but both capability records and package comments retain the eager algorithm contract. Flyology also reuses the eager persisted identity even though version 2 readers reject valid adjacent free blocks produced by the new implementation.
Solution: Publish the lazy TLSF contract and identity
Report the linear worst-case search and deferred release coalescing in standalone and Flyology packages, document the miss-triggered pass, and advance Flyology TLSF to version/schema 3 so cross-version attachment fails at the immutable header. Add standalone capability coverage and a Flyology regression that rejects the complete eager identity.
288b03a2Profiling shows fixed-size allocation cycles spend most of their Best-Fit time splitting a large free block during allocation and immediately removing and coalescing its remainder during release. Fragmented churn repeats related AVL maintenance even when the same physical block can be reused directly.
Solution: Defer Best-Fit coalescing until allocation needs it
Index released blocks directly for exact-size reuse. Before reporting exhaustion, validate the complete physical chain, coalesce every adjacent free run, repair boundary links, and retry the AVL search. Accept the resulting persisted representation on attachment, canonicalize empty arenas during destruction, report the resulting linear worst-case search capability, and cover adjacent frees, alternating gaps, stale handles, attachment validation, and teardown.
41578832Deferred coalescing leaves adjacent free blocks as a valid persisted Best-Fit state. The earlier eager reader rejects that state, but Flyology still advertises the same outer version and schema, so compatibility fails only after entering nested allocator validation.
Solution: Version the Flyology Best-Fit lazy invariant
Advance the Flyology-only adapter version and schema while retaining its magic. This makes older readers reject lazy images at the outer identity boundary and keeps standalone allocator metadata free of application identity.
ec813587The lazy Best-Fit adapter advances its persisted version and schema, but the Flyology data-structure smoke test neither proves rejection of the eager version-2 identity nor reflects the linear fallback and deferred-coalescing capabilities.
Solution: Exercise the Best-Fit version-3 boundary
Temporarily replace a live Best-Fit arena outer identity with the version-2 values and require attachment to fail closed, then restore the image. Assert the truthful lazy-coalescing capabilities for both Best-Fit and TLSF.
e1315591Small allocation cycles repeatedly collapse a released leaf to the root and split the same path on the next allocation. Profiling shows those release stores and recursive rebuilding dominate Buddy's fixed-size cost.
Solution: Coalesce Buddy trees only when allocation needs it
Keep released nodes in the retained split tree, compact free subtrees before a search reports genuine exhaustion, and compact before empty-arena destruction. Add adversarial coverage for half-tree and root reconstruction plus stale handles, and expose the lazy-coalescing capability accurately.
4ad89a24After deferring coalescing, profiling shows fixed-size Buddy allocation is dominated by walking the same split path. Fragmented churn also revisits released orders even though the releasing view already knows a suitable free node.
Solution: Cache one validated free node per Buddy order
Keep process-local node hints in each attached view, validate persisted state before every hinted reuse, and discard all hints during coalescing and detach. Preserve the persisted layout and fall back to the complete tree for stale or missing hints; cover stale hints across independently attached views.
22998f64Buddy still advertises logarithmic eager-coalescing behavior, an eager Flyology reader can attach a retained all-free split tree and falsely report exhaustion for a parent-sized request, and Destroy may release its guard after a partially failed coalescing pass without poisoning the image.
Solution: Harden lazy Buddy capabilities and lifecycle handling
Advertise linear miss search and deferred coalescing, advance only Flyology's outer Buddy identity to version 3, and verify the retained two-leaf compatibility case. Check that an arena is empty before Destroy mutates it and poison any coalescing failure. Compile the standalone project as Ada 2022 for bracket aggregates and extend standalone and Flyology compatibility tests.
66888189Buddy, Best-Fit, and TLSF now defer coalescing until an allocation miss, but the root, standalone, and website guides still advertise their former fast-path and worst-case behavior. The stale prose also describes a process-shared guard even though the standalone algorithms are usable with arbitrary caller-owned storage.
Solution: Document lazy coalescing and view-scoped state
Describe each successful lookup and miss-triggered coalescing path, report the resulting linear worst-case bound, and use attached-view terminology. Record that stored geometry remains stable while the valid state contract changes and Flyology adapters use version-3 identities.
94cd5b57Buddy, Best-Fit, and TLSF now retain free structure on release and coalesce on allocation misses. Behavioral tests cover examples, but no bounded state-machine review checks shared guard ownership, block partitions, index publication, stale view hints, generation-stamped release, false exhaustion, or operation completion across interleavings.
Solution: Model guarded allocator transitions in TLA+
Add a parameterized extraction for the three algorithms with policy-specific splitting and selection, TLSF bin and bitmap state, Buddy view-local hints, lazy coalescing, retry, generations, and fairness. Add safe TLC configurations plus required false-exhaustion, stale-hint, stale-bitmap, stale-release, and nontermination counterexamples, wire them into the authoritative runner, and document the production mapping and abstraction limits.
4dc3e5c0The bounded Buddy, Best-Fit, and TLSF models establish safety and liveness for their extraction, but review alone does not detect when persisted metadata, view-local hints, generations, handles, or size-class computations diverge from the standalone kernels.
Solution: Compare canonical Ada and TLC allocator traces
Add test-only private-state observers and bounded public-operation traces for all three kernels. Model the same traces in TLA+, compare every operation-boundary snapshot byte-for-byte in the authoritative runner, require no-retry liveness counterexamples, and run the conformance boundary in CI while retaining standalone ARM cross-build isolation.
54d77f0fSolution: add a standalone bitmap-slab and contiguous-span allocator, bridge it into Flyology, benchmark it against the existing policies and malloc, and cover its bounded state machine with positive and deliberately broken TLA+ configurations.
239aa004Solution: publish the affected 32-bit bitmap word with release ordering and acquire it before validating the slot generation. Add bounded safe and deliberately broken TLA+ visibility models so TLC reproduces the former stale-handle acceptance.
195b83bfSolution: add a corruption test that makes one large-span head claim its neighboring live allocation, then validate every head and tail descriptor field before reporting capacity or allowing release to mutate the claimed extent.
7a7bc16fThe hero project map, the standalone catalogue, the library ledger, and
llms.txt each name every standalone crate. This branch adds a new one, and
all four lists stop at the crates that existed when the branch was written.
A reader who arrives at the site and looks for what Flyology offers does not
learn that the crate exists.
Solution: Add the crate to the hero map, catalogue, ledger, and llms.txt
Every list keeps its alphabetical order, so the entry sits first among the
standalone crates and the ledger indices shift from S01..S07 to S02..S08.
The crate publishes neither a guide page on this site nor a generated API,
because scripts/docs.sh does not build one and flyology.gpr excludes the
crate from documentation. The entries therefore link to the source tree
only, as the Flyology QUIC entry already does for the same reason. Adding an
api/flyology_allocators/ link would fail the site checker.
Verified with ./scripts/build-site.sh and the site checker, which passes on
141 HTML files, and by grepping the built pages: the hero, catalogue,
ledger, and llms.txt all name the crate.
7dc419f2This branch turns Allocation_Algorithms.Buddy, Best_Fit, and TLSF from
documented packages into generic instantiations of Allocation_Algorithms
.Contract. GNATdoc emits no unit page and no declaration anchor for an
instantiation, so the three API links the guide carries for them now resolve
to nothing. main gained a site link checker after this branch was written,
so the Pages workflow reports all three and fails.
Solution: Present the three instantiations as code rather than links
AGENTS.md asks that the first explanatory mention of a public entity link to
its verified generated GNATdoc unit or declaration entry. No entry is
generated for these three, so there is nothing to link, and pointing them at
the kernels or at Contract would name a different entity than the prose
does. The surrounding Arenas, Contract, Allocation_Capabilities, and View
links are unaffected and still resolve.
Verified with ./scripts/build-site.sh and the site checker, which now passes
on 141 HTML files and 198 total files with zero problems.
9a24bf68This branch adds Allocation_Algorithms.Slab_Span as a fourth arena allocator
and says so in the README, but the guide section that tells a reader how to
choose one still presents three. It names three adapters, describes three
policies, gives three decision rows, and twice states that all three share
one metadata guard. A reader choosing an allocator from this page does not
learn the fourth exists, and the two counts now contradict the README.
Solution: Describe slab/span alongside the other three and correct the counts
The introductory paragraph names the adapter and states its shape: bitmap
slots in fixed runs for small requests, contiguous runs for larger ones, with
descriptors and generation tables outside the managed bytes. A decision row
gives the selection rule and the reclamation behavior a caller can observe:
requests up to half the configured run size take a generation-stamped slot,
larger requests reserve whole runs, release keeps an emptied slab indexed for
reuse, and a miss reclaims empty slabs to obtain the run or span it needs.
The row also states that the run size is an allocator quantum rather than an
operating-system page, because the name invites the opposite assumption.
The half-run threshold matches the kernel, which sends a request to a slab
class when Requested <= Run_Size / 2 and to a span otherwise. Both "all
three" counts become four. The adapter is a generic instantiation, so like
Buddy, Best_Fit, and TLSF it has no generated GNATdoc entry to link.
Verified with ./scripts/build-site.sh and the site checker, which passes on
141 HTML files and 198 total files, and by parsing the built page: tags
balance and the new row renders between TLSF and the compile-time choice.
9a24bf68 → updated to 60de2c69index 8a8e9de51 source commit
60de2c69flyology.gpr imports flyology_allocators.gpr, and the manifest both declares
flyology_allocators as a dependency and pins it to the in-repo directory.
Alire honours pins only in a root crate's manifest, so a consumer resolving
flyology from the index never receives that dependency. Its solution omits
flyology_allocators entirely and gprbuild then fails with "imported project
file flyology_allocators.gpr not found". A crate whose only dependency is
flyology = "~0.1.1-dev" reproduces it in two lines.
Nothing in this repository reports the fault, because the pin satisfies the
dependency for every build performed here. The pin also hid two further
assumptions that only the in-repo directory could satisfy.
Solution: Resolve the allocators dependency from the index
Drop the pin so the declared dependency resolves the same way for this crate
and for its consumers. The published flyology_allocators 0.1.0-dev is built
from this repository at the same commit and the same subdirectory, so the
source reached is identical; only the path by which it is reached changes.
Configure the organization index in this repository's own workflows. They
carried the community index alone, because the pin meant no job here ever
resolved flyology_allocators from an index, and it is published only in the
organization index. Without that step alr reports an incomplete solution
naming flyology_allocators as missing.
Name the excluded documentation project through the imported project rather
than through a repository path. Documentation.Excluded_Project_Files listed
flyology_allocators/flyology_allocators.gpr, which resolved only while the
crate sat in this working tree; as a dependency its project lives in the
Alire build tree under a content hash. Flyology_Allocators'Project_Dir asks
the imported project where it is, so the exclusion holds wherever the
dependency is deployed and the allocator packages stay out of this crate's
generated API, where they would otherwise be documented twice.
Record the rule that produced the fault in the agent guide: no committed
pins in a crate manifest, alr pin in the consuming workspace for unpublished
work, and a declared dependency must resolve from the index. Name the
gprbuild error there too, since that is where the fault appears and it names
a project file rather than a crate.
60de2c69 → updated to 9782c81bindex 75a836be16 source commits
aeb10612The hero project map, the standalone catalogue, the library ledger, and
llms.txt each name every component. Flyology RDF publishes a website, a
guide, and three generated API references at rdf.flyology.org, but the name
appears nowhere in this repository. A reader who arrives at the site and
looks for what Flyology offers does not learn that the project exists.
Solution: Add the project to the hero map, catalogue, ledger, and llms.txt
flyology_rdf depends on flyology_iri only. It needs no runtime preparation
and starts no tasks, so it belongs with the standalone libraries. Every list
keeps its alphabetical order, so the entry sits between Flyology NUMA and
Flyology SIMD and the ledger indices shift from S07..S08 to S08..S09.
The ledger and llms.txt entries carry all three generated references, as the
Flyology Postgres entries already do, because the project site treats the
Notation3 and SPARQL APIs as first-class navigation.
The llms.txt project-structure summary also omitted Flyology Allocators and
Flyology NUMA, which the same file already listed further down. Both names
join the summary here.
Verified with the website-kit site checker, whose output is unchanged from
the pre-change baseline, and by rendering the pages: the hero, catalogue,
and ledger all name the project. The six linked URLs return 200.
2188ee0dThe figure caption claimed "One project, two adoption paths" above two cards
that already carry the same fact in their labels, so the caption restated
what the diagram shows. The two card subtitles were not parallel either:
"Core component" named a role while "No runtime dependency" named a
dependency, so neither pair member helped a reader compare the paths.
The component names below them were plain list items. A reader who found the
name that matched the problem had no way to reach the component from the
diagram and had to scroll to the catalogue to find the same name again.
Solution: Drop the caption, make the subtitles parallel, and link the names
The subtitles now read "Prepare the runtime" and "Add a crate", which state
the two adoption paths as the actions they are. The figure keeps an
accessible name through aria-label, and the figcaption rule leaves the
stylesheet with the element it styled.
Each component name becomes a link to its best available destination, which
is the destination the catalogue below already uses: the project site where
one exists, then the guide, then the generated API, then the source tree.
The pill moves from the list item to the anchor so the whole badge is the
hit target. Links open in a new tab and carry no underline, as requested, so
hover brightens the border and adds a faint fill instead. Each list states
the new-tab behavior once in its aria-label rather than repeating it in
twelve hidden spans.
Verified by rendering the page: every badge computes text-decoration-line
none and cursor pointer, and the hovered badge is distinguishable from its
neighbors. The site checker reports one new line against the baseline,
api/flyology_debug/ from the new Debug badge, which resolves once
scripts/docs.sh generates the GNATdoc output that this partial build omits.
5dfd5d36Two measurement runs that share a machine produce two wrong answers, and
neither run can tell that it happened. The problem is not confined to
benchmarks: a load generator, a soak test, or a profiler makes the machine
loud, and a benchmark needs it quiet. Both want the same thing from opposite
directions, and nothing lets them find each other.
Worse, two runs that each suspend collection when they detect interference
are symmetric controllers observing each other. Each pauses because the other
is loading the machine, both then observe quiet, both resume together, and
both immediately pause again.
Solution: Specify a host CPU lock convention and implement it
The convention is named for the resource rather than for benchmarking, so
that the tools which create load take the same claim as the tools which need
its absence. Conflict detection is flock and nothing else: liveness is the
lock itself, so a holder that dies releases with no cleanup path, no stale
registry entries, and no pid-reuse hazard.
A machine-wide claim takes LOCK_EX on the claim file; a claim on individual
CPUs takes LOCK_SH there and LOCK_EX on one file per CPU. Disjoint core-scoped
claims therefore proceed concurrently while a machine-wide claim excludes them
all, with no registry and no arbiter. Per-CPU claims are acquired in ascending
order, because a total order is what stops two callers with overlapping sets
from deadlocking on each other's partial claims.
The path is a single deterministic default rather than a writability-probing
fallback chain. A chain reintroduces the failure it was meant to prevent:
/run/lock is 1777 on Debian and root-owned 0755 on Red Hat derivatives, so a
root process and an unprivileged process on one machine would probe
differently, land on different files, and each conclude it holds the machine.
$TMPDIR is avoided for the same reason, being per-user on macOS.
File content is diagnostic and is never consulted to decide conflicts. It is
still specified and parseable, one key=value pair per line, because a reader
must be able to name the holder; cwd is included because tool and pid alone do
not distinguish one checkout or CI workspace from another. Readers open the
file without locking, since a shared lock would block behind the very holder
they are trying to identify, and must tolerate content that is empty, stale,
or half-written.
A claim is not proof of exclusivity, and the implementation does not present
it as one. systemd PrivateTmp= bind-mounts a per-service directory over /tmp,
so two services share every CPU and cannot see each other's claim files; that
case is detected from /proc/self/mountinfo and reported as namespace-scoped.
Separate containers are not detectable from inside and need an explicit shared
path. A path can be proven private but never proven machine-wide.
Only the C macros live in the native unit. GNAT's C_Variadic_2 convention
calls open() correctly even on Darwin/AArch64, where variadic arguments are
passed differently from fixed ones, so the protocol, the file grammar, and the
/proc reader are all Ada. The convention compiles on every GNAT this crate
supports, checked against 13.2.2 through 16.1.0 against an alire.toml floor of
13, so no compatibility shim is carried.
Verified by building the crate, and by taking claims on disk: the file matches
the published grammar, parses with awk -F=, is created 0666, and per-CPU files
are named beside the machine file.
13b71a5bCPU_Quiescence samples host counters once, before clock characterization, and
then never looks again. A machine that goes busy halfway through collection
produces a result that reads as clean, and nothing in the measurement, the
console card, or the machine-readable output says otherwise. The gate's own
documentation admits it "cannot prevent new load from arriving after
collection begins", and until now that was the end of the story.
The obvious repair is not available either. Host utilization cannot simply be
compared against a limit during collection the way the preflight does it,
because by then the benchmark thread saturates a core and every sample would
trip the limit.
Solution: Watch for foreign load during collection and say what was found
Foreign load is host busy time minus this process's own CPU time, so load the
benchmark itself creates is correctly not foreign. The estimate scales the
host busy ratio by wall time rather than converting ticks, so the platform
tick length never enters the arithmetic. Cross-checked against top on a loaded
machine, the two agree to within 0.1 percent.
Three responses form a ladder. Observe records and keeps everything. Retake
discards the contaminated window and collects it again. Pause waits for the
host to settle, re-warms, and then collects it again. The re-warm is not
politeness: a resumed run has cold caches, predictors, and frequency state, so
without it the first sample after a pause is the outlier the pause existed to
avoid.
Nothing adjusts a reported statistic. A skew factor derived from observed load
would be invented data, because the cost of interference depends on whether
the contention is an SMT sibling, a shared cache, memory bandwidth, or another
socket, and the same foreign share can mean no slowdown or a large one. Runs
are annotated or repaired, never corrected.
Observation happens between timed samples over windows of whole collection
units, where a unit is one sample, one comparison pair, or one balanced
multi-way round. A response therefore never splits the two halves of a pair
and never lands mid-round, where it would spread interference unevenly across
the cases a round exists to compare fairly. Windows carry a margin over the
configured minimum: sized to only just reach it, ordinary batch jitter drops
them below it and they silently degrade to observation. Host counters are
tick-based, so a window that still falls short is recorded but never acted on.
Discarding a window rolls back what the window changed. Per-sample values are
written by index and survive a retake intact, but the position tally of a
comparison pair and the process-telemetry summaries are running sums, so a
discarded pass gives them back. Without that, a run with eighteen retained
samples and twenty-four retakes would report elapsed time and an average CPU
share covering forty-two samples, most of which were thrown away.
Budgets degrade rather than abort. An exhausted retake or pause budget
continues under Observe and reports it, and paused time is excluded from
Maximum_Sampling_Time because waiting for the host is not collection.
Placement is optional and sharpens attribution: with a strict binding every
other CPU's busy time is foreign outright, and the placed CPU's share is its
busy time minus the placed thread's own. SMT siblings join the watched set,
because a process saturating the sibling of the placed CPU slows the
measurement while leaving the placed CPU's own share clean, which makes
sibling-blind core-scoped observation worse than host-wide observation rather
than better. Placement is Linux-only in practice; Apple Silicon implements no
thread affinity and rejects every request, so the Host_Control documentation
no longer describes Darwin placement as merely advisory.
Placement binds only the calling thread, so another thread of this process can
occupy a watched CPU, where its time is indistinguishable from foreign load.
Each window therefore reads process CPU as well as thread CPU, and their
difference bounds how much of the watched capacity those threads could account
for. Once that bound exceeds the configured foreign limit, the core-scoped
answer can no longer address the question the limit asks: the run drops to
host-wide observation, which is well defined for a multi-threaded process, and
reports Attribution_Diluted. Otherwise a scheduler benchmark run under
placement would spend its retake budget discarding samples over load it
created itself. The window that reveals the dilution is discarded rather than
recorded, because its value was computed under the attribution being
abandoned.
The direction of that fallback is deliberate. Core-scoped attribution
subtracts only the placed thread, which over-reports interference when other
threads share the watched CPUs; subtracting the whole process instead would
deduct time our threads spent on CPUs outside the watched set, under-report
foreign load, and let contaminated data pass as clean. Over-reporting shows up
in the retake and contamination counts; under-reporting shows up nowhere.
The host CPU claim is taken before the preflight gate, since waiting for a
quiet host and then blocking on the claim leaves the quiet verdict stale.
Everything observed reaches the console card, the CSV schema, and the JSON
object. That visibility is the point: a run that had to repair itself must say
so, or a heavily repaired result reads as a quieter machine than the one it
ran on.
Verified with ./scripts/test.sh on Darwin/aarch64 and in an Ubuntu 24.04
container on Linux/aarch64, and under qemu-system-aarch64 across four CPU
topologies. Core-scoped attribution asserts that it closed an observation
window, so the thread-CPU probe it depends on cannot fail silently. Two
assertions were checked against builds that defeat them: one with sibling
collection disabled fails the sibling assertion under cores=2,threads=2 and
passes without SMT, and one with the dilution check disabled fails the
fallback assertion while a task of the test's own process burns CPU. Retake
and pause are driven by a spawned process, because load raised inside this
process is its own CPU time and is subtracted by design.
845bdacawebsite/AGENTS.md governs hand-written pages under guide/**. The interference
section met none of its API-link requirements and several of its style rules.
Every public entity it introduced went unlinked: Interference, Placement,
Host_Lock, the three Interference_Response literals, Maximum_Retakes, Window,
Rewarm_Time, Maximum_Foreign_CPU_Percent, Include_Siblings, Require_Strict,
Attribution_Diluted, and Environment. The same page already links
CPU_Quiescence, Stable_Time, and Timeout, so the new section also read
inconsistently against its own neighbours.
Solution: Link the API entities and apply the three prescribed reviews
The three reviews ran in sequence and are labelled here, since separate review
agents were unavailable.
Technical. "Foreign load is host busy time minus this process's own CPU time"
held only without placement, because core-scoped attribution subtracts the
placed thread rather than the whole process; the sentence now says so, and the
placement paragraph states which CPUs the harness measures. "The re-warm is
required" overstated a configurable field, so the text names Rewarm_Time and
says what a zero value leaves behind. "When the difference exceeds the
configured limit" named neither the limit nor the compared quantity, which
also left Maximum_Foreign_CPU_Percent unlinked. Two harnesses that both pause
do not deadlock as claimed; they alternate between pausing and resuming.
Editorial. "This matters most" and "Without it" both referred back to
something unnamed. Both now name their subject.
Controlled language. "Sharpens attribution" was figurative and is now
"narrows attribution to the placed CPU and its SMT siblings". "A run that
repaired itself reports that fact" personified the run; the report is the
actor, and it states counts.
Verified with ./scripts/build-site.sh and the site checker, which passes 144
HTML files. All fourteen API links resolve to an existing anchor in the
generated GNATdoc output, and each target matches the entry the API search
index gives for that entity. No sentence in the section exceeds the 25-word
review signal.
edf788efThe interference section described three capabilities without saying that a
run acquires none of them unless it asks. BENCH 07 states that contract for
the preflight gate, and the page marks the default value of a choice where one
exists, as it does for Equal_Time and Balanced_Rounds. The new section did
neither, so a reader could not tell that enabling the watch alone changes
nothing about the collected data.
The claim file was also absent. The page named three environment variables but
not the one path that other tools must agree on, leaving the coordination
surface a click away in the convention document.
Solution: State the defaults, the claim path, and the budget interaction
Observe is now marked as the default response, in the form the page already
uses. The closing paragraph mirrors BENCH 07: the watch, placement, and the
host claim are disabled by default, so an existing run acquires no new wait or
failure condition. The opening sentence calls the watch optional where the
reader first meets it.
The claim paragraph names /tmp/host-cpu.lock, the Path field that overrides it
for one run, and the HOST_CPU_LOCK_PATH variable that overrides it for every
tool following the convention. An earlier draft named only the variable, which
implied it was the sole override; the technical review caught that against
Resolved_Path and Try_Acquire.
The Window row gains the limit that qualifies it. A window covers whole units,
so a window spanning many short samples can exceed Maximum_Retakes on its
first discard. Collection then continues under Observe and reports the
exhausted budget, which otherwise looks like the response never ran.
Numeric field defaults are deliberately not repeated here. No other section of
the page tabulates them, every field now links to its generated entry, and a
duplicated value is one that can go stale.
Verified with ./scripts/build-site.sh and the site checker, which passes 144
HTML files. All fifteen API links resolve to an existing anchor, and each
target matches the entry the API search index gives for that entity, including
the ambiguous Path simple name, which resolves to Host_Lock_Policy.Path. No
sentence in the section exceeds the 25-word review signal, and no entity is
linked twice.
4fbe5020recording_smoke drives two benchmarks from four tasks and asserts that the
slow benchmark's recorded median wall time exceeds the fast one's. The
separation comes from delay 0.000_2 against delay 0.000_8. A relative delay
is rounded up to the host timer tick, and on a Linux kernel with a coarse
tick both requests wake at the same boundary near 3 ms. A four-task probe
with no recorder involved shows the first iteration of each task honoring
the request and every later one landing on the same plateau:
0.0002 s: 323 3710 2829 2972 3069 2947 ... (us)
0.0008 s: 1167 3307 2482 3625 2793 3045 ... (us)
The two recorded medians then arrive at 2.98 ms and 2.99 ms, ordered by
scheduling noise alone. On Ubuntu 24.04 with GNAT 16.1.0 on aarch64 the
assertion fails about half the time: 9 of 20 runs on a clean checkout. macOS
resolves the two delays apart, which is why the test has held there. The
independent comparison checked two assertions below rests on the same
separation and fails with it.
Solution: Separate the distributions by work the workers perform
Each worker spins a volatile rotate-and-xor accumulator, 20_000 rounds for
the fast benchmark and twenty times that for the slow one, the idiom the
file's concurrent PMU-session block already uses. That is roughly 33 us
against 680 us, a twentyfold gap read by a nanosecond wall clock, so no
timer resolution enters the measurement. It also costs less than the
sleeping it replaces: the slow workers take about 14 ms rather than 60 ms.
The assertion stands, since the ordering is the invariant worth holding, but
its message now carries both medians so a failure on an unusual host reports
numbers rather than a verdict.
Verified on the failing environment, an Ubuntu 24.04 container with GNAT
16.1.0 on aarch64: 30 of 30 runs pass where HEAD fails 9 of 20 in the same
container, 20 of 20 under a two-CPU quota, and 10 of 10 with 24 competing
spinners on that quota. scripts/test.sh passes three times from a fresh
source export there and once on macOS. A calibration probe over 105 further
trials across those CPU configurations held the ratio between 14x and 30x
with no inversions and Relative_Change_Percent positive throughout.
ae5b52d7Flyology.Observability.Group_Snapshot carries thirty fields covering queue
depths, wait categories, and cumulative counters, but none of them reports how
much of an event loop's time was spent doing work. Pool imbalance is therefore
invisible from outside the process: a group with a deep ready queue and a group
that is idle produce the same evidence once their momentary queue depths match.
Made_Progress detects a stalled loop but not a saturated one, and its comment
already records why a per-dispatch clock read was rejected as the answer.
Solution: Time the poller wait a loop takes when nothing is ready
An event loop enters Wait_Batch only when its ready queue is empty, so the idle
path is both the exact complement of the loop's working time and a path a busy
loop never repeats. The scheduler stamps the wait before releasing the group
lock and closes it after reacquiring, keeping the in-progress wait in the group
so an observer adds it rather than charging a long block as busy time. Nothing
is added to dispatch, context switch, or any other hot path.
Group_Snapshot gains Uptime_Nanoseconds, Idle_Nanoseconds, and Idle_Waits, and
the runtime snapshot ABI moves to version 6. Idle time is clamped to uptime and
both come from one clock reading, so callers can always take the difference or
the ratio. tests/native/process_lifecycle_probe.c pins that ABI and its layout,
and moves with it.
The cost is measured rather than asserted. runtime_callback_bench adds
poller_idle_cycle for the instrumented path and monotonic_clock_read for the
unit of work it adds; benchmarks/idle_wait_rate reports the rate a loop
actually reaches, because the effect is far below what a differential timing
run resolves on this host. On macOS/AArch64 a reading costs about 20 ns, a
socket ping-pong reaches roughly 32,000 to 38,000 waits per second per loop,
and a loop doing nothing else reaches roughly 120,000 to 160,000: under 0.2%
and under 0.7% of a loop's time respectively.
Checks run: ./scripts/test.sh (pass, including the new
observability_utilization_smoke), ./scripts/docs.sh (pass, no new warnings),
./scripts/bench-runtime.sh.
6c4bfc3cStep 10 of the guide lists the readings that diagnose a group, but every one of
them describes queue contents. A reader who wants to know whether an event loop
is saturated has no entry to follow, and the utilization fields the runtime now
publishes appear only in README.md and the generated API reference.
Solution: Document loop utilization where the guide diagnoses a group
A new subsection explains what the runtime measures, why the subtraction is
always valid, and the two readings that look alike: a group whose tasks all
wait resembles a group with nothing to do, and a task that never suspends keeps
its loop out of the poller and reports as fully busy. A decision row places the
measure next to the queue-depth readings it qualifies. The cost paragraph names
the measured figures and the script that reproduces them, rather than asserting
that the accounting is cheap.
The worked example is compiled and run by tests/guide_utilization_example.adb.
The first draft of that example did not compile, because Counter is a modular
subtype and the subtraction needs a use type clause the guide had not shown. A
test main is the only check that keeps a printed example honest about the
visibility a reader must supply.
Checks run: ./scripts/test.sh (pass, including the new
guide_utilization_example), ./scripts/build-site.sh, and check-site.mjs over
the built site as CI runs it.
fb881640The website/AGENTS.md technical review is required for any changed capability,
limit, or timing claim, and running it on the new guide section found four
faults.
Two are wrong rather than unclear. The guide told a reader that `Counter` needs
a `use type` clause because the subtype is modular; the clause is needed
because the operators of a type declared in another package are not directly
visible, so the stated rule teaches the wrong thing. The guide also said an
event loop waits in its poller only when no task can run, but the loop polls
with a zero timeout while work is ready, and the same section counts that
polling as busy time.
The remaining two narrow a claim that had become broader than the code.
"Dispatching, polling with work ready, or running task code" reads as an
exhaustive account of a loop's busy time and omits timer promotion, file
submission, and cancellation processing. That wording appeared in the guide,
README.md, and the Group_Snapshot field documentation, so all three change
together. `Group_Snapshot` and `Snapshot` appeared only inside the code block
and were linked nowhere in prose, which the API-link rule does not allow.
Solution: Correct the two faults and narrow the two broad claims
The editorial and controlled-language passes contributed three further changes:
one repeated paragraph opening, "idle fraction" used before the section
introduces it, and "reading" carrying both a clock read and a snapshot.
Checks run: ./scripts/docs.sh (pass, no new warnings),
./scripts/build-site.sh with check-site.mjs over the built site,
and observability_utilization_smoke, guide_utilization_example, and
observability_smoke rebuilt and re-run.
bca8ad7fscripts/build-site.sh writes the complete site to build/site, but nothing
records how to serve it. Reading a guide page as it will be published means
inventing a static-server invocation each time, and picking a port that does
not collide with a development server already running.
Solution: Record a launch configuration that serves the built site
The entry serves build/site on port 8231. It reads the build output and does
not produce it, so run scripts/build-site.sh first and again after editing
anything under website/.
Checks run: served build/site through the entry and loaded the guide page.
104d406fThe macOS body queries the flat hw.l1dcachesize sysctl. On a heterogeneous
Apple silicon host that name reports the efficiency-core capacity, not the
performance-core capacity: one 12P/4E host reports 65536 bytes for the flat
name against 131072 for hw.perflevel0.l1dcachesize. Flyology_Cachelines.
L1_Data_Cache_Size therefore reports half the performance-core L1d capacity,
and L1_Data_Cache_Slots derives its slot count from that halved value.
The reader also assumed every queried name stores a 64-bit value. It read
each name into one 64-bit object and accepted a short reply, so a 32-bit
value was correct only because Darwin is little-endian and the object was
zero-initialized. The per-level names are 32-bit.
Solution: Prefer the highest performance level and read the reported width
macOS 12 and later describe a host whose cores are not identical as ordered
performance levels, where hw.perflevel0 is the highest-performing level. The
macOS body now queries hw.perflevel0.l1dcachesize and falls back to the flat
name only on a host that publishes no performance level, such as macOS 11 and
earlier or an Intel Mac. Querying hw.nperflevels first would add nothing: the
per-level name exists exactly when the host publishes performance levels.
Hardware_Cache_Line_Size is unchanged, because Darwin publishes no per-level
line size and both Apple silicon core types use the same line.
The public API keeps its Cache_Query_Result surface. Reporting per-level
geometry is a separate design question, and it should not ride along in a
detection correctness fix.
The sysctl reader moves into a new private Flyology_Cachelines.Macos child,
mirroring the existing Flyology_Cachelines.Linux split between mechanism and
policy. It requests each name's stored width before reading the value and
rejects any other width, so a 32-bit name, a 64-bit name, a string, and an
absent name are all handled without depending on byte order. Failed and
malformed queries still degrade to Unavailable rather than raising.
The GNATdoc comments on L1_Data_Cache_Size and L1_Data_Cache_Slots, the crate
README, and the website cachelines guide now state that a host whose cores are
not identical reports one core type, and which one each platform reports. A new
macOS detection test mirrors the Linux one: it exercises the sysctl reader
directly and checks that the reported capacity is the highest performance level
the host publishes.
Checks run: ./flyology_cachelines/scripts/test.sh (the native test now reports
131072 bytes of L1 data cache on a 12P/4E host, and the new macOS detection
test passes) and ./flyology_cachelines/scripts/docs.sh (zero GNATdoc warnings).
d3af8647Flyology_Cachelines.Linux_Testing asserts that both Detect_From_Sysconf and
Detect_From_Sysfs report a value. Only their composition is guaranteed:
glibc answers _SC_LEVEL1_DCACHE_SIZE and _SC_LEVEL1_DCACHE_LINESIZE from
CPUID on x86-64, but reports zero on aarch64, where sysfs is the only
mechanism. Detect already treats sysconf as optional and falls back to
sysfs, so the test is stricter than the code it covers.
The test therefore fails on aarch64 Linux with "sysconf cache detection
failed". CI runs ubuntu-latest on x86-64, where sysconf answers, so the
failure is invisible there. It reproduces on aarch64 Linux, which AGENTS.md
tells contributors to prefer on Apple silicon because it is native and
faster.
Solution: Require the composition and check each available result
Run now requires that at least one mechanism reports a value, and checks
consistency only for a mechanism that reported one. That is what the
production detector promises: it composes sysconf and sysfs, and returns
Unavailable only when neither answers.
Checks run: the crate suite on aarch64 Linux in a container, where the
detection test now passes, and on macOS, where it is not built.
04658ed7Flyology_Cachelines.Linux reads one L1 data-cache geometry: sysconf answers
for the calling CPU, and the sysfs fallback reads cpu0. A host whose cores
are not identical has more than one geometry, and neither mechanism says
which core the reported one belongs to, or that others exist.
macOS already publishes ordered performance levels, so the macOS body can
prefer the highest-performing one. Linux publishes per-CPU descriptions and
no single ordering, so the equivalent information has to be derived before
any public per-core-type query can work on both platforms.
Solution: Group CPUs into core classes and record what ordered them
Detect_Core_Classes enumerates the CPUs the host lists as present, reads
each one's L1 data-cache geometry and capacity, groups CPUs reporting
identical values into a class, and counts each class.
Classes are grouped on capacity and geometry together. Grouping on geometry
alone merges two core types that happen to share an L1 data-cache size,
which real designs do.
Ordering uses whichever key separates the classes, and Core_Classes reports
which one did, so a caller can tell an authoritative order from an inferred
one:
- Host_Reported: the kernel's per-CPU capacity values separated them.
Capacity is the scheduler's own measure of relative CPU performance and
accounts for microarchitecture and clock. Architectures selecting the
generic topology code publish it under cpuN/cpu_capacity.
- Inferred: no capacity separated them, so they are ordered by descending
L1 data-cache capacity. That holds on current hybrid parts and carries
no meaning on a host whose classes differ some other way. x86-64 does
not publish capacity, so its hybrid parts land here.
- Unordered: fewer than two classes, or nothing separated them.
Cores and CPUs are counted separately. SMT siblings share one L1 data cache,
so the cache's sharing list identifies which CPU represents a physical core.
Counting logical CPUs as cores overstates a 24-core host with two threads
per core by a factor of two.
Detection fails closed. A missing or malformed CPU list leaves only CPU 0 to
enumerate, enumeration is bounded, a CPU whose description cannot be read is
skipped, and a host reporting more classes than the table holds reports none
rather than a table that silently omits a class.
Detect_Core_Classes takes the CPU root as a parameter defaulted to the real
sysfs path. Grouping, ordering, and the SMT distinction cannot be reached on
a uniform test host, so the detection test drives them with committed
fixture trees: two classes separated only by capacity, two classes separated
only by geometry with the smaller listed first, SMT pairs sharing one cache,
and a CPU list with a gap.
Nothing calls Detect_Core_Classes outside the test yet. The public
per-core-type query, and the elaboration cost of walking every CPU, belong
to the change that adds that query.
Checks run: the crate suite on aarch64 Linux in a container and on macOS.
Detection was also replayed against captured CPU descriptions from a 48-CPU
x86-64 host with two threads per core, which reports 24 cores against 48
CPUs and no capacity, and from an 80-CPU aarch64 host, which reports 80
cores against 80 CPUs and a published capacity.
ba8438daFlyology_Cachelines reports one L1 data-cache capacity. A host that combines
core types has one per type, so a single value silently describes whichever
type the platform body happened to pick and gives a caller no way to ask
about the others, or to learn that others exist.
The two platforms also disagreed. macOS reported the highest performance
level, while Linux reported whichever cache its own mechanism described:
sysconf for the calling CPU, sysfs for cpu0. The same call therefore meant
different things on the two platforms.
Solution: Make the core class an explicit parameter of every cache query
Core_Class names one class of cores that report a single cache geometry.
Core_Class_Count reports how many the host distinguishes, Core_Class_Cores
and Core_Class_CPUs describe each one, and L1_Data_Cache_Size and
L1_Data_Cache_Slots take the class they describe. The parameter defaults to
Fastest_Core_Class, so existing calls keep compiling and now mean the same
thing on both platforms: the class the host ranks highest.
Core_Class_Ordering reports what produced that rank, because it is a host
fact on some platforms and the crate's inference on others. macOS performance
levels and the Linux per-CPU capacity values are Host_Reported. A host
publishing no rank is Inferred, ordered by descending L1 data-cache capacity.
Fewer than two classes is Unordered.
Cores and CPUs are reported separately. Sibling CPUs under simultaneous
multithreading share one core's L1 data cache, so a 24-core host with two
threads per core reports 24 cores and 48 CPUs in one class.
Hardware_Cache_Line_Size takes no class. macOS publishes no per-class line
size, and the core types of current heterogeneous parts share one line size,
so a per-class query would have one answer to give.
The host is now inspected on first use rather than during elaboration.
Describing every class means reading one description per CPU on Linux, which
measures 14 ms for 48 CPUs and 24 ms for 80. A program that links this crate
and never asks about the host should not pay that at startup. The inspection
is idempotent, so a race on the first query can duplicate work but cannot
produce a value neither caller computed; the ready flag is atomic and is set
after the result is stored.
Flyology_Cachelines.Platform now answers one Detect call describing the whole
host, and the root package body owns the single memoization. The two Linux
bodies share one translation and differ only in what they report when sysfs
describes nothing: the x86-64 body can still read the calling CPU through
CPUID. Flyology_Cachelines.Linux drops its own class and ordering types in
favour of the public ones, so the crate has one Core_Class.
The website cachelines guide is corrected alongside the API. Adding the Class
parameter changed the generated GNATdoc anchors for L1_Data_Cache_Size and
L1_Data_Cache_Slots, so the guide's links to them became dangling fragments;
both now point at the regenerated anchors. The guide also claimed detection
happens during package elaboration, which is no longer true. The new entities
are linked to their generated entries on first mention.
Checks run: the crate suite on macOS, where a 12P/4E host reports two
host-ordered classes of 131072 and 65536 bytes across 12 and 4 cores; on
aarch64 Linux in a container; and on an x86-64 Linux host with 24 cores and
two threads per core, which exercises the CPUID body and reports 49152 bytes
across 24 cores and 48 CPUs. GNATdoc builds with no warnings.
9782c81bFlyology_Cachelines reports L1 but not L2, so a caller sizing a working set
against the level it will actually spill into has nothing to read.
The flat sysctl this crate would naturally have used is wrong in the same way
hw.l1dcachesize was, and by a larger factor. On a 12P/4E Apple silicon host
hw.l2cachesize reports 4194304, the efficiency cluster's cache, against the
performance cluster's 16777216. Adding L2 before core classes existed would
have reproduced the original defect at four times the size instead of two.
Reporting a capacity alone would also mislead. L2 is rarely private: that same
host shares 16 MiB across six performance cores and 4 MiB across four
efficiency cores, so neither figure is a per-core budget.
Solution: Report L2 capacity and how many cores share it, per class
L2_Cache_Size and L2_Sharing_Cores take the same core class as the L1
queries. A sharing count of one means the cache is private to a core; a
larger count means the reported capacity is divided among that many cores.
The GNATdoc on each function points at the other, because reading either
alone invites the per-core mistake.
macOS reads hw.perflevelN.l2cachesize and hw.perflevelN.cpusperl2. Linux
reads the level 2 entry of each CPU's cache description, counting the CPUs
its shared_cpu_list names. Both counts arrive as logical CPUs and become core
counts through the class's own threads-per-core ratio, so a host with two
threads per core does not report twice the cores sharing a cache.
Parsing a CPU list needs more than its leading index, so the Linux body gains
List_Count, which sums the single indexes and inclusive ranges a list names.
Describing L2 costs no extra file reads: the level 2 entry is found in the
same pass over the cache indexes that already locates the level 1 data entry.
The website cachelines guide gains the same pairing, with both names linked
to their generated entries.
Checks run: the crate suite on all three supported targets. A 12P/4E macOS
host reports 16777216 bytes across 6 cores for its performance class and
4194304 across 4 for its efficiency class, and the macOS test asserts that
these come from the performance level rather than the flat name. An aarch64
Linux host reports 16777216 across 6 cores. An x86-64 Linux host with 24
cores and two threads per core reports 2097152 across 1 core, where sysfs
gives 2048K shared by CPUs "0,24"; that run also compiles the CPUID body,
which builds nowhere else. Fixtures cover a per-class shared L2, an L2 shared
across SMT siblings, and a private L2. GNATdoc builds with no warnings.
9782c81b → updated to 7be049fcindex 6e2db0ef1 source commit
7be049fcThe two OpenSSL install steps in the test job carried no bound, so a mirror
that stops answering leaves apt waiting rather than returning an error. On
2026-08-19 run 32297071503, job 96211136305 sat on "Install OpenSSL 3 on
Linux" for 32 minutes and had to be cancelled by hand; it was the only check
blocking PR #49. Its log shows apt-get update fetching noble-security
InRelease at 20:13:04 and producing no further output until cancellation. The
macOS half of the same job passed, so nothing was wrong with the tests.
Left alone the job would have run to GitHub's six-hour default. Nothing else
in either workflow was bounded either: the checkout, the Alire install, the
index configuration, the Java and TLA+ downloads, and the GNATdoc install all
reach the network under the same default, and no job in this repository
declared a timeout at all, unlike the sibling repositories.
Solution: Bound every job and every step that reaches the network
Each job now declares a timeout-minutes, and so does each step that fetches
something. The bounds are generous multiples of what healthy runs measure
over the last 60 successful runs of each workflow, so they can only fire on a
stall. Jobs: test observes at most 17 minutes on macOS and 11 on Linux and is
bounded at 60; proof observes 6 minutes and TLA+ 7, both bounded at 30; the
pages build observes 69 seconds and is bounded at 20, its deploy 10 seconds
and bounded at 15. Steps: everything network-touching is bounded at 10
minutes against observed maxima of 1 to 6 seconds, except the Alire install,
which downloads the toolchain, observes 100 seconds, and is bounded at 15.
A step timeout alone would only have converted the 32-minute hang into a
10-minute one, because the stall is inside a single apt-get update that never
returns. That command is now capped per attempt with timeout(1) and retried
twice with 15 and 30 second backoff, so one unresponsive mirror costs a
retry rather than the job. The install that follows is capped as well but not
retried: a package that cannot be resolved should fail on the first try. The
worst case is three 120-second attempts plus a 10-second kill grace, 45
seconds of backoff, and a 190-second install, which is 10m25s and lands
inside the 12-minute step bound, so the annotation naming the transient
mirror is what the log ends on rather than a bare cancellation.
GNU timeout is used directly rather than scripts/run-with-timeout.sh. That
helper exists for portability to macOS, and this step is Linux-only.
The TLA+ download gains --connect-timeout 30 and --max-time 120 alongside its
existing --retry 3, because curl retries a failed transfer but not a stalled
one, and --max-time resets per attempt. Four bounded attempts stay inside the
step's 10 minutes.
Build, test, proof, and model-checking steps keep no step bound. Their
duration is workload-dependent, a bound sized for them would be a performance
budget rather than a stall detector, and the job timeout already covers them.
Nothing about what is installed, or how anything is built or tested, changes.
Checks run: both workflows parse. The retry script was extracted from the
committed YAML and driven under bash -e against stub apt-get invocations for
five outcomes: first-try success runs one update and one install; two
failures then success runs three updates and installs; three failures exit 1
with the annotation and never install; an install failure propagates its exit
status without retrying; and exit 124, which is what timeout(1) reports when
it kills a stalled command, is treated as retryable. The real commands were
then run in an ubuntu:24.04 container on coreutils 9.4, the runner's version:
the bounded update succeeds, interrupting one mid-flight reports 124, a
retry after that kill still succeeds rather than inheriting a poisoned apt
state, and the bounded install still yields OpenSSL 3.0.13 through pkg-config.
The convention is recorded in AGENTS.md so it holds for new steps, and it
matches the job-level timeouts already used in flyology-http and
gnat-patches, whose install steps want the same treatment.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7be049fc → updated to a53da35dindex b55699621 source commit
a53da35dThe allocator crate is developed alongside the runtime in this repository and
released on the same development line, but the dependency on it is declared as
"*". Nothing stops a dependent from resolving an allocator release built for a
different line, and the index has been carrying a tighter constraint by hand to
compensate.
Solution: Constrain allocators to its development line
Declare the line the allocator crate actually publishes, so the constraint
travels with the manifest instead of being reapplied wherever it is indexed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.1.1-devBounded in-memory tracing and gauges for Ada
91d6a0f6 → updated to 9a24bf68index 510cfcf639 source commits
40849230The functional loopback checks use retry intervals too narrow for occasional macOS hosted-runner stalls. An early client exception also waits behind cancellation coordination and loses the operation that failed, leaving only a generic lifecycle assertion.
Solution: tolerate stalls and report DNS smoke stages
Use functional smoke deadlines that accommodate runner scheduling, let coordination observe early client completion, and report the execution model and operation stage for unexpected exceptions. The dedicated DNS resilience smoke retains narrow deadline coverage.
347604c6Session logs are distributed across project checkouts, worktrees, sister
repositories, and nested agent sessions. Their token counters also contain
cached input, repeated cumulative records, inherited history, and duplicated
Claude message entries, making straightforward aggregation inaccurate.
Solution: add a portable incremental AI usage audit
Add a self-contained PEP 723 Python script that discovers the workspace from
its containing repository and scans associated Codex and Claude Code logs,
including worktrees, subagents, and workflows.
Deduplicate cumulative and inherited records, categorize base input, cache
reads and writes, output and reasoning tokens, and estimate API-style costs
using model-specific pricing. Persist JSON cursors and daily aggregates so
subsequent runs process only new log records.
Provide summary, daily, weekly, provider, and model breakdowns with JSON
output, date filtering, configurable paths and timezone, state rebuilding,
and integrity diagnostics.
73925055The landing page, navigation, guide index, and machine-readable documentation center Flyology Runtime while standalone libraries share the project identity without depending on it. Visitors cannot reliably distinguish the runtime core, runtime-dependent protocol libraries, and independently adoptable components, and several component documentation paths are missing or placed under the wrong section.
Solution: Present Flyology as the project and Runtime as its core
Reorganize the landing page around the project umbrella, add focused Runtime and Libraries pages, move Guide, Architecture, and the main API under Runtime, separate standalone documentation from the Runtime guide, link each published component Guide and API, fix the scrollable guide topic rail, and align README, product, design, journal, build assertions, and llms.txt with the same dependency model.
6cb6887fThe redesigned project pages repeat the experimental label in several nearby locations while the curated Libraries directory has no path to the complete package index. Readers see redundant maturity warnings but cannot easily reach version, dependency, availability, and compiler-build records for every published package.
Solution: Clarify maturity guidance and link the complete crate index
Keep experimental status in the primary explanatory context, make constraints and adoption guidance more direct, and link crates.flyology.org from the Libraries introduction, footer, and llms.txt documentation map.
4738d656Only the Libraries page exposes the complete package catalogue in its footer. Readers arriving on Runtime, Guide, Architecture, Journal, or the project overview have no equivalent utility path to package versions, dependencies, and availability records.
Solution: Add the crate index to every shared footer
Place the Crate index utility link first in each hand-written footer, including the journal entry template, while retaining every page-specific navigation link.
5b7c3925Check_Parent_Cancellation_Is_Prompt in tests/task_scope_faults_smoke requires
every one of 16 rounds to propagate parent cancellation within 5 ms.
Propagation crosses two lightweight-task wakes, and a contended shared runner
can deschedule either of them for milliseconds, so the check reports host
scheduling as often as it reports the mechanism. CI run 32100324387 failed
this assertion on its first attempt and passed on a rerun of the identical
commit 71df9ed, whose only change is .github/workflows/pages.yml. Locally,
96 spinning processes on 16 cores plus nice -n 20 reproduce the failure in
2 of 30 runs, and 1280 rounds measured under that load put the median at
78 us with 0.9 per cent of rounds past 5 ms: the mechanism is prompt and the
bound is reporting the host. A bare pragma Assert also names only a source
line, so a real regression is indistinguishable from a stalled round.
Solution: Require most rounds to be prompt, bound the rest, and report them
The check now measures all 16 rounds, requires at least 10 of them within
1 ms, and requires no round past a 0.5 s sanity ceiling. That is a stronger
claim about the mechanism rather than a weaker one. The retired 10 ms polling
monitor spreads its latency across its period and places about 1.6 of 16
rounds inside 1 ms, so it clears the new bar about once in two million runs
against once in sixty-five thousand for the old whole-round 5 ms bound;
reinstating that monitor put 0 of 16 rounds inside 1 ms and failed the new
check on all five idle runs. Under the load that failed the old check in
11 per cent of runs, the new check passed 60 of 60. A failing round set is
printed to standard error so the next failure names the rounds and their
latencies instead of only the assertion line.
857820c4Problem: Cancellation promptness is asserted as a per-round wall-clock bound
5dfa0d25The site check that validates local links and fragment targets runs only in
the Pages workflow, which triggers on a push to main and on manual dispatch.
No pull-request workflow builds the site, and ci.yml has no site job, so a
change that breaks a link passes every check a reviewer sees, merges, and
then fails the Pages build on main. The site stops publishing at the point
where the mistake is hardest to attribute.
The hand-written pages carry 457 links into generated GNATdoc entries across
eleven guide and architecture pages. GNATdoc names each entry by a hash of
the declaration, so changing a public subprogram's parameters gives it a new
anchor and silently strands every hand-written link to it. Editing the
surrounding documentation comment does not, which makes the failure easy to
miss: prose edits are safe, and a signature change quietly is not.
Solution: Build and check the site on pull requests
The Pages workflow now also runs on pull_request. The build job is
unchanged for a push, and on a pull request it builds the site and runs the
same link check without touching Pages: configuring Pages, uploading the
artifact, and the deploy job are all skipped. A pull request therefore needs
no Pages permission, which also keeps the job usable from a fork.
Concurrency now groups per ref. One group for every event serialized
unrelated pull requests behind each other and behind a deploy. Pushes to main
still share one group and are still never cancelled; a pull request cancels
its own superseded runs.
This adds a toolchain and GNATdoc build to each pull request, which is the
cost of resolving generated anchors: the check can only confirm a fragment
exists by generating the page that defines it.
Checks run: the workflow parses, has no tabs, guards exactly the three
Pages-specific steps, and keeps the link check unguarded. check-site.mjs was
confirmed to catch this class of breakage against a page carrying one stale
API fragment, where it reports "missing fragment target" and exits non-zero.
This pull request triggers the new job, so its own run is the end-to-end
check.
252f4fd7A machine with more than one processor package attaches memory to each of
them, and memory attached to the package a thread runs on is reached faster
than memory attached to another. Nothing in this repository reports that
structure. Ada itself does not describe it either: System.Multiprocessors
covers processors and dispatching, and the language has no notion of a
memory node at all.
Without it, a program that wants its work near its memory has nothing to
place against.
Solution: Adopt flyology_numa as a nested standalone crate that reports it
The crate reads the host description and reports memory nodes, the
processors attached to each, firmware-declared distances, per-node memory,
and processor packages. It does not place memory; that is a separate
question this crate does not answer.
Three decisions shape the interface.
Topology queries are total. A host with no memory-node structure reports one
node holding every processor, because such a host genuinely has one memory
domain. Support distinguishes a description that was read from the host from
that single-domain description. Individual facts a host does not answer use
the existing variant-record convention with Value_Or, so a caller names its
own fallback rather than receiving a plausible number.
Online and permitted nodes are reported separately. A container is shown the
whole machine's node list while a control group restricts what it may
allocate on, so the host description alone does not establish permission.
Allowed_Nodes intersects the description with Mems_allowed_list from the
process status file.
Node and processor numbers are the host's own, and sparse. They match sysfs
paths and numactl output, so a reported number is the number to look up
elsewhere. To_CPU performs the Ada processor mapping GNAT applies unchecked,
and declines when the host's numbering has a gap the mapping cannot carry.
A description this crate cannot read is reported as a shortfall rather than
guessed at. Support.Complete goes false when any part of the description was
refused: a node or processor number beyond the represented range, a list
naming more ranges than the reader holds, an unreadable processor list, or a
permitted-node list the host named and the reader did not understand. The
distinction that matters is between a node with no processor attached, which
is an ordinary memory expander, and a node whose processor list could not be
read, which is not known to have none. A description that names nodes but
attaches no processor to any of them is refused outright, because every
machine has a processor.
The sysfs reader names the directories it reads instead of assuming them, so
it is host-independent text processing that compiles and runs everywhere.
This departs from flyology_cachelines, which keeps all Linux code under a
Linux source directory and tests only the host it runs on. The interesting
cases cannot be reached that way: a development machine has one node and a
container on one is single-node too. Twelve recorded descriptions under
tests/fixtures cover two packages of one node each, two packages each
divided into two memory nodes, a node carrying memory with no processor
attached, sparse node numbering, a control-group restriction, a gap in
processor numbering, an older host naming only its ordinary memory, and five
descriptions the reader must refuse or report as incomplete. All are
exercised on macOS, which has none of them.
Set algebra lives in its own unit rather than the root body because
discovery runs during the root package's elaboration and cannot call back
into the body it is elaborating. Each step of the discovery chain names the
body it needs with pragma Elaborate, so the ordering does not rest on the
binder inferring it. Elaborate_All would not serve: its closure reaches back
through the platform spec to the root body itself.
A processor list is bounded at one range per processor rather than at 64,
because a host that enumerates processors round-robin across its nodes gives
each node a list of single-processor ranges.
Verified with ./flyology_numa/scripts/test.sh on macOS aarch64 from a clean
tree: both the host suite and the fixture suite pass, and clean builds of
the linux, macos, and unsupported platform bodies through the crate's
host-selection scenario variable emit no warnings, style diagnostics, or
errors.
376d2481The crate describes which memory nodes a host has and which processors are
attached to each. A program that wants its work near its memory can read
that and then do nothing with it: there is no way to ask that a region's
pages be drawn from a chosen node.
Solution: Add Flyology_NUMA.Placement over the kernel's memory-policy calls
Placement is a separate package from the reporting one because the two fail
differently. Reading a description always succeeds; acting on one often does
not. Support reports which of three states applies before anything is
attempted, and every operation reports its own outcome rather than raising.
The third state is the one worth having. A host may have no placement
interface, or it may have one this process is refused. A container sandbox
commonly refuses these calls while the machine underneath has memory nodes
that other processes are using, and reporting that as a host without the
facility would be wrong. Denied is therefore distinct from Unsupported_Host.
The two are told apart by what the host said about itself rather than by the
error number alone, because a sandbox may answer a refused call by claiming
the call does not exist: a host that described its own memory nodes has the
calls that act on them, so a refusal from such a host is a denial.
Support is settled on the first call that needs it rather than while the
package elaborates. A sandbox that answers a refused call by ending the
process would otherwise end any program that merely links this crate, even
one that never places memory.
Apply_To places a range, Apply_To_Thread places what the calling thread
allocates from then on, Node_Of_Address reports where a page ended up, and
Permitted_Nodes asks the host afresh rather than reusing what it said
earlier. A range must begin on a page boundary because placement acts on
whole pages; an unaligned range is refused before the host is asked.
Three details of the kernel interface are easy to get wrong and silent when
got wrong, so each is handled deliberately.
The node-bit count is decremented by the kernel before the mask is read, so
passing the number of bits meant would drop the highest node. Placement
passes one more than that, which names every node this crate represents
while still asking for only the eight bytes the mask has. Reading the
permitted nodes is bounded differently again: the kernel refuses a count
below the number of nodes the host could ever have, counting nodes that are
merely possible rather than online, so that read uses a buffer covering the
largest node count Linux can be built for.
Two policies take no node set, and the kernel rejects either of them when
one arrives. Passing the set a caller naturally has to hand would therefore
fail, so the set is dropped before the host sees it and the interface's
promise that it is ignored is kept.
The preferred policy takes a single node, not a set. The interface documents
that only the lowest-numbered node is preferred rather than implying the
whole set contributes.
No C library wraps these calls. Their manual pages document a separate
library's wrappers, not the one every program already links, so they are
reached through the generic system-call entry point. That entry point is
variadic, which is one of the reasons this repository admits C, but GNAT's
variadic convention reaches it correctly without a C file and keeps the
crate free of one, as flyology_cachelines is.
Call numbers differ per architecture, and not only in value: the x86-64
table and the table newer architectures share disagree on the order of
get_mempolicy and set_mempolicy. Calling by a number from the wrong table
reaches a working but unrelated kernel service, which is worse than not
calling at all, so each table is written out separately and an architecture
whose numbers are not recorded disables placement rather than borrowing
them.
That failure is silent by nature, so the host suite has an invariant that
exposes it: a host that described its own memory nodes has the calls that
act on them, so reporting no way to place memory means the calls were not
reached. Building for one architecture with another's table was checked to
fail on exactly that assertion rather than passing quietly, and passing a
node set with a policy that takes none was checked to fail before the set
was dropped.
Verified on macOS aarch64, x86-64 Linux, and AArch64 Linux. macOS reports
Unsupported_Host and places nothing. Both Linux architectures report
Supported and complete every operation: every policy applied to a range and
to the calling thread, a written page reported on an online node, an
unaligned range refused, and an empty node set refused for a policy that
needs one. Builds of the macos, linux and unsupported platform bodies across
x86-64, AArch64 and an architecture with no recorded table emit no warnings,
style diagnostics, or errors.
41e2d423Flyology_NUMA.Placement acts on an address range. Using it to put an object
on a chosen node means obtaining page-aligned memory, placing it, carving
objects out of it, and releasing it afterwards, all in the caller's code and
all outside the language's own way of saying where memory comes from.
Ada already has that way: the storage pool of an access type. Nothing here
supplied one.
Solution: Add Flyology_NUMA.Pools, a storage pool whose subpools are nodes
An access type names the pool and each allocation names the node, so where
an object lives is written at the point it is created rather than arranged
around it:
Item : constant Sample_Access := new (On_Node (Arena, Near)) Sample'(...);
Subpools are the part of the language that fits this. A pool alone would fix
the node when the access type was declared; a subpool is chosen per
allocation, which is what naming a node needs to be.
The pool obtains whole pages from the host rather than taking memory from
the ordinary heap. Heap memory may begin partway into a page whose remainder
belongs to something else, and placing that page would move memory this
crate does not own. Pages are placed before any of them is handed out, since
placing them afterwards would leave whatever had already been written where
the host first put it.
Memory is handed out by advancing through pages already obtained, and goes
back to the host when a subpool is deallocated or the pool is finalized.
Freeing a single object does nothing. That suits memory built up and
discarded together, which is what a node-bound arena usually is, and the
interface says so rather than implying a general allocator.
Ownership of the pages is left to the runtime's own teardown rather than
taken over. The runtime keeps the records it needs to finalize an object
inside the very pages that object lives in, and a pool that gave its pages
back first would take those records with them; a pool holding anything with
finalization would then fail on the way out of its own scope. Deallocating
one subpool likewise forgets it, because the runtime has already detached it
by then and a pool that still believed it owned it could never serve that
node again.
Only the policies that name a node can be given to a pool. The two that name
none place nothing on the node the subpool stands for, so a pool built on
one would report memory placed there that never was.
Placement_Reached separates a host that placed the memory from one that
could not, and says which of the two it means: the host accepted the
request. Under a bound policy that puts the pages on the node; under a
preferred one the host stays free to draw from elsewhere. Allocation
succeeds either way, on the reasoning that memory in the wrong place still
runs while no memory does not.
A pool is not task safe, and the interface says so. Two tasks allocating
from one subpool at once can be handed the same address.
Verified on macOS aarch64, x86-64 Linux, and AArch64 Linux, and inside
guests built to have several memory nodes, which no machine to hand does.
scripts/multinode-check.sh boots a two-node guest and a four-node guest
whose distances are uneven and one of whose nodes carries memory with no
processor attached, then runs the host suite inside them. It was run on
both x86-64 and AArch64, each emulating its own architecture, so that the
placement calls a guest makes are the ones its architecture's table names. Those runs are
what exercise choosing between nodes at all: memory bound to each node in
turn and confirmed held by that node, including the node with no processor;
interleaving spread over every node rather than drawn from one; and a node
shown further from another than from itself.
The suite gained cases for the two failures described above, and each was
confirmed to fail before the change and pass after. Builds of the macos,
linux and unsupported platform bodies across x86-64, AArch64 and an
architecture with no recorded placement table emit no warnings, style
diagnostics, or errors.
a416d8dc./scripts/test.sh runs each crate's own entry point by path, so the file
has to carry the executable bit. flyology_numa/scripts/test.sh was recorded
without it and the whole suite stopped there, reporting only that a command
could not be run.
Running the script as an argument to a shell, which is what a person does
while writing one, works either way and hides this.
Solution: Record the executable bit, as the other crates' entry points do
550bc612The crate ships, and nothing outside its own directory says so. It has no
guide page, no generated API reference, and no entry in the library ledger
or the machine-readable index. A reader who finds the other standalone
crates on the site does not find this one.
Solution: Adopt the crate into the documentation and the site
The crate now builds its own GNATdoc reference, and the root documentation
run builds and asserts it alongside the other crates. Publishing it required
documenting the pool primitives the runtime calls, which had no comments,
and describing the pool's discriminants in prose: @field names a record
component, and a discriminant of a private type is neither.
The package renaming that shortened System.Storage_Pools.Subpools in the
public part is now in the private part. GNATdoc reported the public renaming
on every documentation run, and the full name appears twice in the visible
declarations rather than in every generated page.
The guide page follows the order a reader needs: add the crate, read the
node structure, use the permitted nodes rather than the online ones, handle
a host with one memory domain, place memory, allocate from a node, and read
distances and packages for what they say rather than what they suggest.
Every API link on that page was resolved through the generated search index
rather than by reading the generated markup, which does not put an entity's
name where a reader of the HTML would expect it.
The library ledger keeps its alphabetical order, so the new entry is S05 and
the two entries after it move up.
Verified with ./scripts/docs.sh, which builds the reference with no
undocumented-entity warnings, and ./scripts/build-site.sh followed by the
site checker, which reports 144 HTML files and 201 total files. The crate's
own suite still passes.
ae1e3ddfThe project map in the hero names each standalone library, and the
catalogue below it links each one. Both lists stop at the crates that
existed before this one. A reader who arrives at the site and looks for what
Flyology offers does not learn that the crate exists.
Solution: Add the crate to the hero project map and the catalogue
Both lists keep their alphabetical order, so the entry sits between IRI and
SIMD. The catalogue entry links to the guide, as the entries for the other
crates with a guide on this site do.
Verified with ./scripts/build-site.sh and the site checker, and by fetching
the built pages: the hero and catalogue both list the crate, and all 39
internal links on its guide page resolve.
aa8b12edThe page opens with a short definition in a callout and then goes straight
to adding the crate. A reader who does not already know what a memory node
is learns the interface before the reason for it, and a reader who does know
still gets no picture of the machine the crate describes.
Solution: Open with a section that explains memory nodes and draws one
The section states what changes when a machine has more than one processor
package, draws a two-node machine with a local and a remote access, and says
plainly when placement is worth doing at all: a program that fits in cache,
or one that allocates little, does not gain from it. The callout it replaces
said less.
The diagram uses only the classes the figure context defines. An earlier
draft used a class that context does not style, and SVG then fell back to a
solid black fill. That reads as deliberate against a dark background and
wrong against a light one, which is how it survived a first look.
Both themes were checked by computed style rather than by eye. The
containers take the page's own surface and border colours, so they follow
the theme instead of fighting it.
Verified with ./scripts/build-site.sh and the site checker.
7b70cbf6Buddy, best-fit, and TLSF live inside the main package hierarchy, so bare-board runtimes cannot reuse them without also accepting Flyology identity, mapping, and shared-memory concerns.
Solution: Extract allocation algorithms into a standalone crate
Add flyology_allocators 0.1.0-dev with caller-owned regions, timed contention retries, native and ARM bare-board verification, and optimized memory primitives. Keep magic and schema policy in a thin Flyology adapter, pin the new crate from Flyology, and update integration tests and documentation.
7bf86246The extracted algorithms lack a reproducible benchmark against the platform allocator, making hot-path regressions and algorithm tradeoffs difficult to measure.
Solution: Add a dedicated flyology_bench allocator shootout
Add an optional hosted benchmark subcrate with fixed-size and fragmented-churn workloads comparing malloc, Buddy, Best-Fit, and TLSF in balanced paired rounds. Keep benchmark dependencies outside the bare-board crate closure and document release and smoke workflows.
be44ac54Profiling shows recursive Buddy allocation and release spend most of their time rebuilding and validating structure-relative node addresses. Release also validates each handle twice before its first mutation, making small allocation cycles substantially slower than the other standalone algorithms.
Solution: Cache the validated Buddy node-table binding
Retain the process-local node-table base in the attached view after validating its complete native range, use bounded node-relative addressing on hot paths, clear the binding on detach, and remove the redundant release validation while preserving validation before mutation.
283d5e71Profiles of fixed allocation cycles show TLSF spending most of its time in free-list insertion and removal, block initialization, and handle validation. Each logical operation repeatedly recomputes and validates the same block base before touching adjacent scalar fields, leaving the constant-time algorithm dominated by address machinery.
Solution: Reuse validated block bases across TLSF hot paths
Validate each referenced block extent once, carry its process-local base through list mutation, splitting, coalescing, and handle release, and keep stored link, size, generation, state, and bitmap checks intact. Avoid a redundant ready-state check while the arena guard is held and replace size-class division by its exact power-of-two shift.
e9199d66Profiling shows fixed-layout address validation, duplicate split and coalesce writes, and repeated AVL balance reads dominate Best-Fit allocation and release. This leaves both fixed cycles and fragmented churn substantially slower than necessary.
Solution: Streamline checked Best-Fit metadata operations
Cache the last complete block-header location in the local view, use a fixed-field address path that retains attachment and complete-header bounds checks, avoid tree metadata writes that insertion immediately replaces, and compute AVL balance inputs once per ancestor. The stored layout, persisted validation, guard behavior, handle generations, and bare-board dependency boundary remain unchanged.
05d2eb83The benchmark project selects release switches only for its own sources. Imported flyology_allocators units are compiled without optimization, making comparisons against the optimized system malloc misleading and overstating allocator hot-path costs.
Solution: Force a clean optimized benchmark closure
Build the benchmark and all imported Ada sources through gprbuild with explicit O3, debug symbols, Ada 2022, warnings, and validity checks. Document why the runner must apply switches across project boundaries.
77e1d163GNAT reports parenthesized array aggregates as obsolescent when the benchmark closure is compiled as Ada 2022, obscuring useful build diagnostics.
Solution: Adopt Ada 2022 bracketed array aggregates
Convert Flyology Bench array initializers and expressions to bracket syntax while retaining parentheses for record aggregates. Keep the obsolescent warning category enabled so future occurrences remain visible.
77ce4c04Release repeatedly discovers physical neighbors and removes and reinserts free-list entries even when the next allocation can reuse the same size class. This makes steady fixed-size and churn workloads pay fragmentation-recovery costs on every operation.
Solution: Defer TLSF coalescing until allocation requires it
Return released blocks directly to their size class and perform a bounded, checked physical coalescing pass only after a normal TLSF search misses or before destroying an empty arena. Preserve boundary tags and bitmap/list validation, and cover adjacent runs, allocated gaps, attachment, stale handles, and complete teardown with adversarial tests.
562bdd61Lazy coalescing makes worst-case allocation linear and no longer joins neighbors during release, but both capability records and package comments retain the eager algorithm contract. Flyology also reuses the eager persisted identity even though version 2 readers reject valid adjacent free blocks produced by the new implementation.
Solution: Publish the lazy TLSF contract and identity
Report the linear worst-case search and deferred release coalescing in standalone and Flyology packages, document the miss-triggered pass, and advance Flyology TLSF to version/schema 3 so cross-version attachment fails at the immutable header. Add standalone capability coverage and a Flyology regression that rejects the complete eager identity.
288b03a2Profiling shows fixed-size allocation cycles spend most of their Best-Fit time splitting a large free block during allocation and immediately removing and coalescing its remainder during release. Fragmented churn repeats related AVL maintenance even when the same physical block can be reused directly.
Solution: Defer Best-Fit coalescing until allocation needs it
Index released blocks directly for exact-size reuse. Before reporting exhaustion, validate the complete physical chain, coalesce every adjacent free run, repair boundary links, and retry the AVL search. Accept the resulting persisted representation on attachment, canonicalize empty arenas during destruction, report the resulting linear worst-case search capability, and cover adjacent frees, alternating gaps, stale handles, attachment validation, and teardown.
41578832Deferred coalescing leaves adjacent free blocks as a valid persisted Best-Fit state. The earlier eager reader rejects that state, but Flyology still advertises the same outer version and schema, so compatibility fails only after entering nested allocator validation.
Solution: Version the Flyology Best-Fit lazy invariant
Advance the Flyology-only adapter version and schema while retaining its magic. This makes older readers reject lazy images at the outer identity boundary and keeps standalone allocator metadata free of application identity.
ec813587The lazy Best-Fit adapter advances its persisted version and schema, but the Flyology data-structure smoke test neither proves rejection of the eager version-2 identity nor reflects the linear fallback and deferred-coalescing capabilities.
Solution: Exercise the Best-Fit version-3 boundary
Temporarily replace a live Best-Fit arena outer identity with the version-2 values and require attachment to fail closed, then restore the image. Assert the truthful lazy-coalescing capabilities for both Best-Fit and TLSF.
e1315591Small allocation cycles repeatedly collapse a released leaf to the root and split the same path on the next allocation. Profiling shows those release stores and recursive rebuilding dominate Buddy's fixed-size cost.
Solution: Coalesce Buddy trees only when allocation needs it
Keep released nodes in the retained split tree, compact free subtrees before a search reports genuine exhaustion, and compact before empty-arena destruction. Add adversarial coverage for half-tree and root reconstruction plus stale handles, and expose the lazy-coalescing capability accurately.
4ad89a24After deferring coalescing, profiling shows fixed-size Buddy allocation is dominated by walking the same split path. Fragmented churn also revisits released orders even though the releasing view already knows a suitable free node.
Solution: Cache one validated free node per Buddy order
Keep process-local node hints in each attached view, validate persisted state before every hinted reuse, and discard all hints during coalescing and detach. Preserve the persisted layout and fall back to the complete tree for stale or missing hints; cover stale hints across independently attached views.
22998f64Buddy still advertises logarithmic eager-coalescing behavior, an eager Flyology reader can attach a retained all-free split tree and falsely report exhaustion for a parent-sized request, and Destroy may release its guard after a partially failed coalescing pass without poisoning the image.
Solution: Harden lazy Buddy capabilities and lifecycle handling
Advertise linear miss search and deferred coalescing, advance only Flyology's outer Buddy identity to version 3, and verify the retained two-leaf compatibility case. Check that an arena is empty before Destroy mutates it and poison any coalescing failure. Compile the standalone project as Ada 2022 for bracket aggregates and extend standalone and Flyology compatibility tests.
66888189Buddy, Best-Fit, and TLSF now defer coalescing until an allocation miss, but the root, standalone, and website guides still advertise their former fast-path and worst-case behavior. The stale prose also describes a process-shared guard even though the standalone algorithms are usable with arbitrary caller-owned storage.
Solution: Document lazy coalescing and view-scoped state
Describe each successful lookup and miss-triggered coalescing path, report the resulting linear worst-case bound, and use attached-view terminology. Record that stored geometry remains stable while the valid state contract changes and Flyology adapters use version-3 identities.
94cd5b57Buddy, Best-Fit, and TLSF now retain free structure on release and coalesce on allocation misses. Behavioral tests cover examples, but no bounded state-machine review checks shared guard ownership, block partitions, index publication, stale view hints, generation-stamped release, false exhaustion, or operation completion across interleavings.
Solution: Model guarded allocator transitions in TLA+
Add a parameterized extraction for the three algorithms with policy-specific splitting and selection, TLSF bin and bitmap state, Buddy view-local hints, lazy coalescing, retry, generations, and fairness. Add safe TLC configurations plus required false-exhaustion, stale-hint, stale-bitmap, stale-release, and nontermination counterexamples, wire them into the authoritative runner, and document the production mapping and abstraction limits.
4dc3e5c0The bounded Buddy, Best-Fit, and TLSF models establish safety and liveness for their extraction, but review alone does not detect when persisted metadata, view-local hints, generations, handles, or size-class computations diverge from the standalone kernels.
Solution: Compare canonical Ada and TLC allocator traces
Add test-only private-state observers and bounded public-operation traces for all three kernels. Model the same traces in TLA+, compare every operation-boundary snapshot byte-for-byte in the authoritative runner, require no-retry liveness counterexamples, and run the conformance boundary in CI while retaining standalone ARM cross-build isolation.
54d77f0fSolution: add a standalone bitmap-slab and contiguous-span allocator, bridge it into Flyology, benchmark it against the existing policies and malloc, and cover its bounded state machine with positive and deliberately broken TLA+ configurations.
239aa004Solution: publish the affected 32-bit bitmap word with release ordering and acquire it before validating the slot generation. Add bounded safe and deliberately broken TLA+ visibility models so TLC reproduces the former stale-handle acceptance.
195b83bfSolution: add a corruption test that makes one large-span head claim its neighboring live allocation, then validate every head and tail descriptor field before reporting capacity or allowing release to mutate the claimed extent.
7a7bc16fThe hero project map, the standalone catalogue, the library ledger, and
llms.txt each name every standalone crate. This branch adds a new one, and
all four lists stop at the crates that existed when the branch was written.
A reader who arrives at the site and looks for what Flyology offers does not
learn that the crate exists.
Solution: Add the crate to the hero map, catalogue, ledger, and llms.txt
Every list keeps its alphabetical order, so the entry sits first among the
standalone crates and the ledger indices shift from S01..S07 to S02..S08.
The crate publishes neither a guide page on this site nor a generated API,
because scripts/docs.sh does not build one and flyology.gpr excludes the
crate from documentation. The entries therefore link to the source tree
only, as the Flyology QUIC entry already does for the same reason. Adding an
api/flyology_allocators/ link would fail the site checker.
Verified with ./scripts/build-site.sh and the site checker, which passes on
141 HTML files, and by grepping the built pages: the hero, catalogue,
ledger, and llms.txt all name the crate.
7dc419f2This branch turns Allocation_Algorithms.Buddy, Best_Fit, and TLSF from
documented packages into generic instantiations of Allocation_Algorithms
.Contract. GNATdoc emits no unit page and no declaration anchor for an
instantiation, so the three API links the guide carries for them now resolve
to nothing. main gained a site link checker after this branch was written,
so the Pages workflow reports all three and fails.
Solution: Present the three instantiations as code rather than links
AGENTS.md asks that the first explanatory mention of a public entity link to
its verified generated GNATdoc unit or declaration entry. No entry is
generated for these three, so there is nothing to link, and pointing them at
the kernels or at Contract would name a different entity than the prose
does. The surrounding Arenas, Contract, Allocation_Capabilities, and View
links are unaffected and still resolve.
Verified with ./scripts/build-site.sh and the site checker, which now passes
on 141 HTML files and 198 total files with zero problems.
9a24bf68This branch adds Allocation_Algorithms.Slab_Span as a fourth arena allocator
and says so in the README, but the guide section that tells a reader how to
choose one still presents three. It names three adapters, describes three
policies, gives three decision rows, and twice states that all three share
one metadata guard. A reader choosing an allocator from this page does not
learn the fourth exists, and the two counts now contradict the README.
Solution: Describe slab/span alongside the other three and correct the counts
The introductory paragraph names the adapter and states its shape: bitmap
slots in fixed runs for small requests, contiguous runs for larger ones, with
descriptors and generation tables outside the managed bytes. A decision row
gives the selection rule and the reclamation behavior a caller can observe:
requests up to half the configured run size take a generation-stamped slot,
larger requests reserve whole runs, release keeps an emptied slab indexed for
reuse, and a miss reclaims empty slabs to obtain the run or span it needs.
The row also states that the run size is an allocator quantum rather than an
operating-system page, because the name invites the opposite assumption.
The half-run threshold matches the kernel, which sends a request to a slab
class when Requested <= Run_Size / 2 and to a span otherwise. Both "all
three" counts become four. The adapter is a generic instantiation, so like
Buddy, Best_Fit, and TLSF it has no generated GNATdoc entry to link.
Verified with ./scripts/build-site.sh and the site checker, which passes on
141 HTML files and 198 total files, and by parsing the built page: tags
balance and the new row renders between TLSF and the compile-time choice.
9a24bf68 → updated to 60de2c69index 8a8e9de51 source commit
60de2c69flyology.gpr imports flyology_allocators.gpr, and the manifest both declares
flyology_allocators as a dependency and pins it to the in-repo directory.
Alire honours pins only in a root crate's manifest, so a consumer resolving
flyology from the index never receives that dependency. Its solution omits
flyology_allocators entirely and gprbuild then fails with "imported project
file flyology_allocators.gpr not found". A crate whose only dependency is
flyology = "~0.1.1-dev" reproduces it in two lines.
Nothing in this repository reports the fault, because the pin satisfies the
dependency for every build performed here. The pin also hid two further
assumptions that only the in-repo directory could satisfy.
Solution: Resolve the allocators dependency from the index
Drop the pin so the declared dependency resolves the same way for this crate
and for its consumers. The published flyology_allocators 0.1.0-dev is built
from this repository at the same commit and the same subdirectory, so the
source reached is identical; only the path by which it is reached changes.
Configure the organization index in this repository's own workflows. They
carried the community index alone, because the pin meant no job here ever
resolved flyology_allocators from an index, and it is published only in the
organization index. Without that step alr reports an incomplete solution
naming flyology_allocators as missing.
Name the excluded documentation project through the imported project rather
than through a repository path. Documentation.Excluded_Project_Files listed
flyology_allocators/flyology_allocators.gpr, which resolved only while the
crate sat in this working tree; as a dependency its project lives in the
Alire build tree under a content hash. Flyology_Allocators'Project_Dir asks
the imported project where it is, so the exclusion holds wherever the
dependency is deployed and the allocator packages stay out of this crate's
generated API, where they would otherwise be documented twice.
Record the rule that produced the fault in the agent guide: no committed
pins in a crate manifest, alr pin in the consuming workspace for unpublished
work, and a declared dependency must resolve from the index. Name the
gprbuild error there too, since that is where the fault appears and it names
a project file rather than a crate.
60de2c69 → updated to 9782c81bindex 75a836be16 source commits
aeb10612The hero project map, the standalone catalogue, the library ledger, and
llms.txt each name every component. Flyology RDF publishes a website, a
guide, and three generated API references at rdf.flyology.org, but the name
appears nowhere in this repository. A reader who arrives at the site and
looks for what Flyology offers does not learn that the project exists.
Solution: Add the project to the hero map, catalogue, ledger, and llms.txt
flyology_rdf depends on flyology_iri only. It needs no runtime preparation
and starts no tasks, so it belongs with the standalone libraries. Every list
keeps its alphabetical order, so the entry sits between Flyology NUMA and
Flyology SIMD and the ledger indices shift from S07..S08 to S08..S09.
The ledger and llms.txt entries carry all three generated references, as the
Flyology Postgres entries already do, because the project site treats the
Notation3 and SPARQL APIs as first-class navigation.
The llms.txt project-structure summary also omitted Flyology Allocators and
Flyology NUMA, which the same file already listed further down. Both names
join the summary here.
Verified with the website-kit site checker, whose output is unchanged from
the pre-change baseline, and by rendering the pages: the hero, catalogue,
and ledger all name the project. The six linked URLs return 200.
2188ee0dThe figure caption claimed "One project, two adoption paths" above two cards
that already carry the same fact in their labels, so the caption restated
what the diagram shows. The two card subtitles were not parallel either:
"Core component" named a role while "No runtime dependency" named a
dependency, so neither pair member helped a reader compare the paths.
The component names below them were plain list items. A reader who found the
name that matched the problem had no way to reach the component from the
diagram and had to scroll to the catalogue to find the same name again.
Solution: Drop the caption, make the subtitles parallel, and link the names
The subtitles now read "Prepare the runtime" and "Add a crate", which state
the two adoption paths as the actions they are. The figure keeps an
accessible name through aria-label, and the figcaption rule leaves the
stylesheet with the element it styled.
Each component name becomes a link to its best available destination, which
is the destination the catalogue below already uses: the project site where
one exists, then the guide, then the generated API, then the source tree.
The pill moves from the list item to the anchor so the whole badge is the
hit target. Links open in a new tab and carry no underline, as requested, so
hover brightens the border and adds a faint fill instead. Each list states
the new-tab behavior once in its aria-label rather than repeating it in
twelve hidden spans.
Verified by rendering the page: every badge computes text-decoration-line
none and cursor pointer, and the hovered badge is distinguishable from its
neighbors. The site checker reports one new line against the baseline,
api/flyology_debug/ from the new Debug badge, which resolves once
scripts/docs.sh generates the GNATdoc output that this partial build omits.
5dfd5d36Two measurement runs that share a machine produce two wrong answers, and
neither run can tell that it happened. The problem is not confined to
benchmarks: a load generator, a soak test, or a profiler makes the machine
loud, and a benchmark needs it quiet. Both want the same thing from opposite
directions, and nothing lets them find each other.
Worse, two runs that each suspend collection when they detect interference
are symmetric controllers observing each other. Each pauses because the other
is loading the machine, both then observe quiet, both resume together, and
both immediately pause again.
Solution: Specify a host CPU lock convention and implement it
The convention is named for the resource rather than for benchmarking, so
that the tools which create load take the same claim as the tools which need
its absence. Conflict detection is flock and nothing else: liveness is the
lock itself, so a holder that dies releases with no cleanup path, no stale
registry entries, and no pid-reuse hazard.
A machine-wide claim takes LOCK_EX on the claim file; a claim on individual
CPUs takes LOCK_SH there and LOCK_EX on one file per CPU. Disjoint core-scoped
claims therefore proceed concurrently while a machine-wide claim excludes them
all, with no registry and no arbiter. Per-CPU claims are acquired in ascending
order, because a total order is what stops two callers with overlapping sets
from deadlocking on each other's partial claims.
The path is a single deterministic default rather than a writability-probing
fallback chain. A chain reintroduces the failure it was meant to prevent:
/run/lock is 1777 on Debian and root-owned 0755 on Red Hat derivatives, so a
root process and an unprivileged process on one machine would probe
differently, land on different files, and each conclude it holds the machine.
$TMPDIR is avoided for the same reason, being per-user on macOS.
File content is diagnostic and is never consulted to decide conflicts. It is
still specified and parseable, one key=value pair per line, because a reader
must be able to name the holder; cwd is included because tool and pid alone do
not distinguish one checkout or CI workspace from another. Readers open the
file without locking, since a shared lock would block behind the very holder
they are trying to identify, and must tolerate content that is empty, stale,
or half-written.
A claim is not proof of exclusivity, and the implementation does not present
it as one. systemd PrivateTmp= bind-mounts a per-service directory over /tmp,
so two services share every CPU and cannot see each other's claim files; that
case is detected from /proc/self/mountinfo and reported as namespace-scoped.
Separate containers are not detectable from inside and need an explicit shared
path. A path can be proven private but never proven machine-wide.
Only the C macros live in the native unit. GNAT's C_Variadic_2 convention
calls open() correctly even on Darwin/AArch64, where variadic arguments are
passed differently from fixed ones, so the protocol, the file grammar, and the
/proc reader are all Ada. The convention compiles on every GNAT this crate
supports, checked against 13.2.2 through 16.1.0 against an alire.toml floor of
13, so no compatibility shim is carried.
Verified by building the crate, and by taking claims on disk: the file matches
the published grammar, parses with awk -F=, is created 0666, and per-CPU files
are named beside the machine file.
13b71a5bCPU_Quiescence samples host counters once, before clock characterization, and
then never looks again. A machine that goes busy halfway through collection
produces a result that reads as clean, and nothing in the measurement, the
console card, or the machine-readable output says otherwise. The gate's own
documentation admits it "cannot prevent new load from arriving after
collection begins", and until now that was the end of the story.
The obvious repair is not available either. Host utilization cannot simply be
compared against a limit during collection the way the preflight does it,
because by then the benchmark thread saturates a core and every sample would
trip the limit.
Solution: Watch for foreign load during collection and say what was found
Foreign load is host busy time minus this process's own CPU time, so load the
benchmark itself creates is correctly not foreign. The estimate scales the
host busy ratio by wall time rather than converting ticks, so the platform
tick length never enters the arithmetic. Cross-checked against top on a loaded
machine, the two agree to within 0.1 percent.
Three responses form a ladder. Observe records and keeps everything. Retake
discards the contaminated window and collects it again. Pause waits for the
host to settle, re-warms, and then collects it again. The re-warm is not
politeness: a resumed run has cold caches, predictors, and frequency state, so
without it the first sample after a pause is the outlier the pause existed to
avoid.
Nothing adjusts a reported statistic. A skew factor derived from observed load
would be invented data, because the cost of interference depends on whether
the contention is an SMT sibling, a shared cache, memory bandwidth, or another
socket, and the same foreign share can mean no slowdown or a large one. Runs
are annotated or repaired, never corrected.
Observation happens between timed samples over windows of whole collection
units, where a unit is one sample, one comparison pair, or one balanced
multi-way round. A response therefore never splits the two halves of a pair
and never lands mid-round, where it would spread interference unevenly across
the cases a round exists to compare fairly. Windows carry a margin over the
configured minimum: sized to only just reach it, ordinary batch jitter drops
them below it and they silently degrade to observation. Host counters are
tick-based, so a window that still falls short is recorded but never acted on.
Discarding a window rolls back what the window changed. Per-sample values are
written by index and survive a retake intact, but the position tally of a
comparison pair and the process-telemetry summaries are running sums, so a
discarded pass gives them back. Without that, a run with eighteen retained
samples and twenty-four retakes would report elapsed time and an average CPU
share covering forty-two samples, most of which were thrown away.
Budgets degrade rather than abort. An exhausted retake or pause budget
continues under Observe and reports it, and paused time is excluded from
Maximum_Sampling_Time because waiting for the host is not collection.
Placement is optional and sharpens attribution: with a strict binding every
other CPU's busy time is foreign outright, and the placed CPU's share is its
busy time minus the placed thread's own. SMT siblings join the watched set,
because a process saturating the sibling of the placed CPU slows the
measurement while leaving the placed CPU's own share clean, which makes
sibling-blind core-scoped observation worse than host-wide observation rather
than better. Placement is Linux-only in practice; Apple Silicon implements no
thread affinity and rejects every request, so the Host_Control documentation
no longer describes Darwin placement as merely advisory.
Placement binds only the calling thread, so another thread of this process can
occupy a watched CPU, where its time is indistinguishable from foreign load.
Each window therefore reads process CPU as well as thread CPU, and their
difference bounds how much of the watched capacity those threads could account
for. Once that bound exceeds the configured foreign limit, the core-scoped
answer can no longer address the question the limit asks: the run drops to
host-wide observation, which is well defined for a multi-threaded process, and
reports Attribution_Diluted. Otherwise a scheduler benchmark run under
placement would spend its retake budget discarding samples over load it
created itself. The window that reveals the dilution is discarded rather than
recorded, because its value was computed under the attribution being
abandoned.
The direction of that fallback is deliberate. Core-scoped attribution
subtracts only the placed thread, which over-reports interference when other
threads share the watched CPUs; subtracting the whole process instead would
deduct time our threads spent on CPUs outside the watched set, under-report
foreign load, and let contaminated data pass as clean. Over-reporting shows up
in the retake and contamination counts; under-reporting shows up nowhere.
The host CPU claim is taken before the preflight gate, since waiting for a
quiet host and then blocking on the claim leaves the quiet verdict stale.
Everything observed reaches the console card, the CSV schema, and the JSON
object. That visibility is the point: a run that had to repair itself must say
so, or a heavily repaired result reads as a quieter machine than the one it
ran on.
Verified with ./scripts/test.sh on Darwin/aarch64 and in an Ubuntu 24.04
container on Linux/aarch64, and under qemu-system-aarch64 across four CPU
topologies. Core-scoped attribution asserts that it closed an observation
window, so the thread-CPU probe it depends on cannot fail silently. Two
assertions were checked against builds that defeat them: one with sibling
collection disabled fails the sibling assertion under cores=2,threads=2 and
passes without SMT, and one with the dilution check disabled fails the
fallback assertion while a task of the test's own process burns CPU. Retake
and pause are driven by a spawned process, because load raised inside this
process is its own CPU time and is subtracted by design.
845bdacawebsite/AGENTS.md governs hand-written pages under guide/**. The interference
section met none of its API-link requirements and several of its style rules.
Every public entity it introduced went unlinked: Interference, Placement,
Host_Lock, the three Interference_Response literals, Maximum_Retakes, Window,
Rewarm_Time, Maximum_Foreign_CPU_Percent, Include_Siblings, Require_Strict,
Attribution_Diluted, and Environment. The same page already links
CPU_Quiescence, Stable_Time, and Timeout, so the new section also read
inconsistently against its own neighbours.
Solution: Link the API entities and apply the three prescribed reviews
The three reviews ran in sequence and are labelled here, since separate review
agents were unavailable.
Technical. "Foreign load is host busy time minus this process's own CPU time"
held only without placement, because core-scoped attribution subtracts the
placed thread rather than the whole process; the sentence now says so, and the
placement paragraph states which CPUs the harness measures. "The re-warm is
required" overstated a configurable field, so the text names Rewarm_Time and
says what a zero value leaves behind. "When the difference exceeds the
configured limit" named neither the limit nor the compared quantity, which
also left Maximum_Foreign_CPU_Percent unlinked. Two harnesses that both pause
do not deadlock as claimed; they alternate between pausing and resuming.
Editorial. "This matters most" and "Without it" both referred back to
something unnamed. Both now name their subject.
Controlled language. "Sharpens attribution" was figurative and is now
"narrows attribution to the placed CPU and its SMT siblings". "A run that
repaired itself reports that fact" personified the run; the report is the
actor, and it states counts.
Verified with ./scripts/build-site.sh and the site checker, which passes 144
HTML files. All fourteen API links resolve to an existing anchor in the
generated GNATdoc output, and each target matches the entry the API search
index gives for that entity. No sentence in the section exceeds the 25-word
review signal.
edf788efThe interference section described three capabilities without saying that a
run acquires none of them unless it asks. BENCH 07 states that contract for
the preflight gate, and the page marks the default value of a choice where one
exists, as it does for Equal_Time and Balanced_Rounds. The new section did
neither, so a reader could not tell that enabling the watch alone changes
nothing about the collected data.
The claim file was also absent. The page named three environment variables but
not the one path that other tools must agree on, leaving the coordination
surface a click away in the convention document.
Solution: State the defaults, the claim path, and the budget interaction
Observe is now marked as the default response, in the form the page already
uses. The closing paragraph mirrors BENCH 07: the watch, placement, and the
host claim are disabled by default, so an existing run acquires no new wait or
failure condition. The opening sentence calls the watch optional where the
reader first meets it.
The claim paragraph names /tmp/host-cpu.lock, the Path field that overrides it
for one run, and the HOST_CPU_LOCK_PATH variable that overrides it for every
tool following the convention. An earlier draft named only the variable, which
implied it was the sole override; the technical review caught that against
Resolved_Path and Try_Acquire.
The Window row gains the limit that qualifies it. A window covers whole units,
so a window spanning many short samples can exceed Maximum_Retakes on its
first discard. Collection then continues under Observe and reports the
exhausted budget, which otherwise looks like the response never ran.
Numeric field defaults are deliberately not repeated here. No other section of
the page tabulates them, every field now links to its generated entry, and a
duplicated value is one that can go stale.
Verified with ./scripts/build-site.sh and the site checker, which passes 144
HTML files. All fifteen API links resolve to an existing anchor, and each
target matches the entry the API search index gives for that entity, including
the ambiguous Path simple name, which resolves to Host_Lock_Policy.Path. No
sentence in the section exceeds the 25-word review signal, and no entity is
linked twice.
4fbe5020recording_smoke drives two benchmarks from four tasks and asserts that the
slow benchmark's recorded median wall time exceeds the fast one's. The
separation comes from delay 0.000_2 against delay 0.000_8. A relative delay
is rounded up to the host timer tick, and on a Linux kernel with a coarse
tick both requests wake at the same boundary near 3 ms. A four-task probe
with no recorder involved shows the first iteration of each task honoring
the request and every later one landing on the same plateau:
0.0002 s: 323 3710 2829 2972 3069 2947 ... (us)
0.0008 s: 1167 3307 2482 3625 2793 3045 ... (us)
The two recorded medians then arrive at 2.98 ms and 2.99 ms, ordered by
scheduling noise alone. On Ubuntu 24.04 with GNAT 16.1.0 on aarch64 the
assertion fails about half the time: 9 of 20 runs on a clean checkout. macOS
resolves the two delays apart, which is why the test has held there. The
independent comparison checked two assertions below rests on the same
separation and fails with it.
Solution: Separate the distributions by work the workers perform
Each worker spins a volatile rotate-and-xor accumulator, 20_000 rounds for
the fast benchmark and twenty times that for the slow one, the idiom the
file's concurrent PMU-session block already uses. That is roughly 33 us
against 680 us, a twentyfold gap read by a nanosecond wall clock, so no
timer resolution enters the measurement. It also costs less than the
sleeping it replaces: the slow workers take about 14 ms rather than 60 ms.
The assertion stands, since the ordering is the invariant worth holding, but
its message now carries both medians so a failure on an unusual host reports
numbers rather than a verdict.
Verified on the failing environment, an Ubuntu 24.04 container with GNAT
16.1.0 on aarch64: 30 of 30 runs pass where HEAD fails 9 of 20 in the same
container, 20 of 20 under a two-CPU quota, and 10 of 10 with 24 competing
spinners on that quota. scripts/test.sh passes three times from a fresh
source export there and once on macOS. A calibration probe over 105 further
trials across those CPU configurations held the ratio between 14x and 30x
with no inversions and Relative_Change_Percent positive throughout.
ae5b52d7Flyology.Observability.Group_Snapshot carries thirty fields covering queue
depths, wait categories, and cumulative counters, but none of them reports how
much of an event loop's time was spent doing work. Pool imbalance is therefore
invisible from outside the process: a group with a deep ready queue and a group
that is idle produce the same evidence once their momentary queue depths match.
Made_Progress detects a stalled loop but not a saturated one, and its comment
already records why a per-dispatch clock read was rejected as the answer.
Solution: Time the poller wait a loop takes when nothing is ready
An event loop enters Wait_Batch only when its ready queue is empty, so the idle
path is both the exact complement of the loop's working time and a path a busy
loop never repeats. The scheduler stamps the wait before releasing the group
lock and closes it after reacquiring, keeping the in-progress wait in the group
so an observer adds it rather than charging a long block as busy time. Nothing
is added to dispatch, context switch, or any other hot path.
Group_Snapshot gains Uptime_Nanoseconds, Idle_Nanoseconds, and Idle_Waits, and
the runtime snapshot ABI moves to version 6. Idle time is clamped to uptime and
both come from one clock reading, so callers can always take the difference or
the ratio. tests/native/process_lifecycle_probe.c pins that ABI and its layout,
and moves with it.
The cost is measured rather than asserted. runtime_callback_bench adds
poller_idle_cycle for the instrumented path and monotonic_clock_read for the
unit of work it adds; benchmarks/idle_wait_rate reports the rate a loop
actually reaches, because the effect is far below what a differential timing
run resolves on this host. On macOS/AArch64 a reading costs about 20 ns, a
socket ping-pong reaches roughly 32,000 to 38,000 waits per second per loop,
and a loop doing nothing else reaches roughly 120,000 to 160,000: under 0.2%
and under 0.7% of a loop's time respectively.
Checks run: ./scripts/test.sh (pass, including the new
observability_utilization_smoke), ./scripts/docs.sh (pass, no new warnings),
./scripts/bench-runtime.sh.
6c4bfc3cStep 10 of the guide lists the readings that diagnose a group, but every one of
them describes queue contents. A reader who wants to know whether an event loop
is saturated has no entry to follow, and the utilization fields the runtime now
publishes appear only in README.md and the generated API reference.
Solution: Document loop utilization where the guide diagnoses a group
A new subsection explains what the runtime measures, why the subtraction is
always valid, and the two readings that look alike: a group whose tasks all
wait resembles a group with nothing to do, and a task that never suspends keeps
its loop out of the poller and reports as fully busy. A decision row places the
measure next to the queue-depth readings it qualifies. The cost paragraph names
the measured figures and the script that reproduces them, rather than asserting
that the accounting is cheap.
The worked example is compiled and run by tests/guide_utilization_example.adb.
The first draft of that example did not compile, because Counter is a modular
subtype and the subtraction needs a use type clause the guide had not shown. A
test main is the only check that keeps a printed example honest about the
visibility a reader must supply.
Checks run: ./scripts/test.sh (pass, including the new
guide_utilization_example), ./scripts/build-site.sh, and check-site.mjs over
the built site as CI runs it.
fb881640The website/AGENTS.md technical review is required for any changed capability,
limit, or timing claim, and running it on the new guide section found four
faults.
Two are wrong rather than unclear. The guide told a reader that `Counter` needs
a `use type` clause because the subtype is modular; the clause is needed
because the operators of a type declared in another package are not directly
visible, so the stated rule teaches the wrong thing. The guide also said an
event loop waits in its poller only when no task can run, but the loop polls
with a zero timeout while work is ready, and the same section counts that
polling as busy time.
The remaining two narrow a claim that had become broader than the code.
"Dispatching, polling with work ready, or running task code" reads as an
exhaustive account of a loop's busy time and omits timer promotion, file
submission, and cancellation processing. That wording appeared in the guide,
README.md, and the Group_Snapshot field documentation, so all three change
together. `Group_Snapshot` and `Snapshot` appeared only inside the code block
and were linked nowhere in prose, which the API-link rule does not allow.
Solution: Correct the two faults and narrow the two broad claims
The editorial and controlled-language passes contributed three further changes:
one repeated paragraph opening, "idle fraction" used before the section
introduces it, and "reading" carrying both a clock read and a snapshot.
Checks run: ./scripts/docs.sh (pass, no new warnings),
./scripts/build-site.sh with check-site.mjs over the built site,
and observability_utilization_smoke, guide_utilization_example, and
observability_smoke rebuilt and re-run.
bca8ad7fscripts/build-site.sh writes the complete site to build/site, but nothing
records how to serve it. Reading a guide page as it will be published means
inventing a static-server invocation each time, and picking a port that does
not collide with a development server already running.
Solution: Record a launch configuration that serves the built site
The entry serves build/site on port 8231. It reads the build output and does
not produce it, so run scripts/build-site.sh first and again after editing
anything under website/.
Checks run: served build/site through the entry and loaded the guide page.
104d406fThe macOS body queries the flat hw.l1dcachesize sysctl. On a heterogeneous
Apple silicon host that name reports the efficiency-core capacity, not the
performance-core capacity: one 12P/4E host reports 65536 bytes for the flat
name against 131072 for hw.perflevel0.l1dcachesize. Flyology_Cachelines.
L1_Data_Cache_Size therefore reports half the performance-core L1d capacity,
and L1_Data_Cache_Slots derives its slot count from that halved value.
The reader also assumed every queried name stores a 64-bit value. It read
each name into one 64-bit object and accepted a short reply, so a 32-bit
value was correct only because Darwin is little-endian and the object was
zero-initialized. The per-level names are 32-bit.
Solution: Prefer the highest performance level and read the reported width
macOS 12 and later describe a host whose cores are not identical as ordered
performance levels, where hw.perflevel0 is the highest-performing level. The
macOS body now queries hw.perflevel0.l1dcachesize and falls back to the flat
name only on a host that publishes no performance level, such as macOS 11 and
earlier or an Intel Mac. Querying hw.nperflevels first would add nothing: the
per-level name exists exactly when the host publishes performance levels.
Hardware_Cache_Line_Size is unchanged, because Darwin publishes no per-level
line size and both Apple silicon core types use the same line.
The public API keeps its Cache_Query_Result surface. Reporting per-level
geometry is a separate design question, and it should not ride along in a
detection correctness fix.
The sysctl reader moves into a new private Flyology_Cachelines.Macos child,
mirroring the existing Flyology_Cachelines.Linux split between mechanism and
policy. It requests each name's stored width before reading the value and
rejects any other width, so a 32-bit name, a 64-bit name, a string, and an
absent name are all handled without depending on byte order. Failed and
malformed queries still degrade to Unavailable rather than raising.
The GNATdoc comments on L1_Data_Cache_Size and L1_Data_Cache_Slots, the crate
README, and the website cachelines guide now state that a host whose cores are
not identical reports one core type, and which one each platform reports. A new
macOS detection test mirrors the Linux one: it exercises the sysctl reader
directly and checks that the reported capacity is the highest performance level
the host publishes.
Checks run: ./flyology_cachelines/scripts/test.sh (the native test now reports
131072 bytes of L1 data cache on a 12P/4E host, and the new macOS detection
test passes) and ./flyology_cachelines/scripts/docs.sh (zero GNATdoc warnings).
d3af8647Flyology_Cachelines.Linux_Testing asserts that both Detect_From_Sysconf and
Detect_From_Sysfs report a value. Only their composition is guaranteed:
glibc answers _SC_LEVEL1_DCACHE_SIZE and _SC_LEVEL1_DCACHE_LINESIZE from
CPUID on x86-64, but reports zero on aarch64, where sysfs is the only
mechanism. Detect already treats sysconf as optional and falls back to
sysfs, so the test is stricter than the code it covers.
The test therefore fails on aarch64 Linux with "sysconf cache detection
failed". CI runs ubuntu-latest on x86-64, where sysconf answers, so the
failure is invisible there. It reproduces on aarch64 Linux, which AGENTS.md
tells contributors to prefer on Apple silicon because it is native and
faster.
Solution: Require the composition and check each available result
Run now requires that at least one mechanism reports a value, and checks
consistency only for a mechanism that reported one. That is what the
production detector promises: it composes sysconf and sysfs, and returns
Unavailable only when neither answers.
Checks run: the crate suite on aarch64 Linux in a container, where the
detection test now passes, and on macOS, where it is not built.
04658ed7Flyology_Cachelines.Linux reads one L1 data-cache geometry: sysconf answers
for the calling CPU, and the sysfs fallback reads cpu0. A host whose cores
are not identical has more than one geometry, and neither mechanism says
which core the reported one belongs to, or that others exist.
macOS already publishes ordered performance levels, so the macOS body can
prefer the highest-performing one. Linux publishes per-CPU descriptions and
no single ordering, so the equivalent information has to be derived before
any public per-core-type query can work on both platforms.
Solution: Group CPUs into core classes and record what ordered them
Detect_Core_Classes enumerates the CPUs the host lists as present, reads
each one's L1 data-cache geometry and capacity, groups CPUs reporting
identical values into a class, and counts each class.
Classes are grouped on capacity and geometry together. Grouping on geometry
alone merges two core types that happen to share an L1 data-cache size,
which real designs do.
Ordering uses whichever key separates the classes, and Core_Classes reports
which one did, so a caller can tell an authoritative order from an inferred
one:
- Host_Reported: the kernel's per-CPU capacity values separated them.
Capacity is the scheduler's own measure of relative CPU performance and
accounts for microarchitecture and clock. Architectures selecting the
generic topology code publish it under cpuN/cpu_capacity.
- Inferred: no capacity separated them, so they are ordered by descending
L1 data-cache capacity. That holds on current hybrid parts and carries
no meaning on a host whose classes differ some other way. x86-64 does
not publish capacity, so its hybrid parts land here.
- Unordered: fewer than two classes, or nothing separated them.
Cores and CPUs are counted separately. SMT siblings share one L1 data cache,
so the cache's sharing list identifies which CPU represents a physical core.
Counting logical CPUs as cores overstates a 24-core host with two threads
per core by a factor of two.
Detection fails closed. A missing or malformed CPU list leaves only CPU 0 to
enumerate, enumeration is bounded, a CPU whose description cannot be read is
skipped, and a host reporting more classes than the table holds reports none
rather than a table that silently omits a class.
Detect_Core_Classes takes the CPU root as a parameter defaulted to the real
sysfs path. Grouping, ordering, and the SMT distinction cannot be reached on
a uniform test host, so the detection test drives them with committed
fixture trees: two classes separated only by capacity, two classes separated
only by geometry with the smaller listed first, SMT pairs sharing one cache,
and a CPU list with a gap.
Nothing calls Detect_Core_Classes outside the test yet. The public
per-core-type query, and the elaboration cost of walking every CPU, belong
to the change that adds that query.
Checks run: the crate suite on aarch64 Linux in a container and on macOS.
Detection was also replayed against captured CPU descriptions from a 48-CPU
x86-64 host with two threads per core, which reports 24 cores against 48
CPUs and no capacity, and from an 80-CPU aarch64 host, which reports 80
cores against 80 CPUs and a published capacity.
ba8438daFlyology_Cachelines reports one L1 data-cache capacity. A host that combines
core types has one per type, so a single value silently describes whichever
type the platform body happened to pick and gives a caller no way to ask
about the others, or to learn that others exist.
The two platforms also disagreed. macOS reported the highest performance
level, while Linux reported whichever cache its own mechanism described:
sysconf for the calling CPU, sysfs for cpu0. The same call therefore meant
different things on the two platforms.
Solution: Make the core class an explicit parameter of every cache query
Core_Class names one class of cores that report a single cache geometry.
Core_Class_Count reports how many the host distinguishes, Core_Class_Cores
and Core_Class_CPUs describe each one, and L1_Data_Cache_Size and
L1_Data_Cache_Slots take the class they describe. The parameter defaults to
Fastest_Core_Class, so existing calls keep compiling and now mean the same
thing on both platforms: the class the host ranks highest.
Core_Class_Ordering reports what produced that rank, because it is a host
fact on some platforms and the crate's inference on others. macOS performance
levels and the Linux per-CPU capacity values are Host_Reported. A host
publishing no rank is Inferred, ordered by descending L1 data-cache capacity.
Fewer than two classes is Unordered.
Cores and CPUs are reported separately. Sibling CPUs under simultaneous
multithreading share one core's L1 data cache, so a 24-core host with two
threads per core reports 24 cores and 48 CPUs in one class.
Hardware_Cache_Line_Size takes no class. macOS publishes no per-class line
size, and the core types of current heterogeneous parts share one line size,
so a per-class query would have one answer to give.
The host is now inspected on first use rather than during elaboration.
Describing every class means reading one description per CPU on Linux, which
measures 14 ms for 48 CPUs and 24 ms for 80. A program that links this crate
and never asks about the host should not pay that at startup. The inspection
is idempotent, so a race on the first query can duplicate work but cannot
produce a value neither caller computed; the ready flag is atomic and is set
after the result is stored.
Flyology_Cachelines.Platform now answers one Detect call describing the whole
host, and the root package body owns the single memoization. The two Linux
bodies share one translation and differ only in what they report when sysfs
describes nothing: the x86-64 body can still read the calling CPU through
CPUID. Flyology_Cachelines.Linux drops its own class and ordering types in
favour of the public ones, so the crate has one Core_Class.
The website cachelines guide is corrected alongside the API. Adding the Class
parameter changed the generated GNATdoc anchors for L1_Data_Cache_Size and
L1_Data_Cache_Slots, so the guide's links to them became dangling fragments;
both now point at the regenerated anchors. The guide also claimed detection
happens during package elaboration, which is no longer true. The new entities
are linked to their generated entries on first mention.
Checks run: the crate suite on macOS, where a 12P/4E host reports two
host-ordered classes of 131072 and 65536 bytes across 12 and 4 cores; on
aarch64 Linux in a container; and on an x86-64 Linux host with 24 cores and
two threads per core, which exercises the CPUID body and reports 49152 bytes
across 24 cores and 48 CPUs. GNATdoc builds with no warnings.
9782c81bFlyology_Cachelines reports L1 but not L2, so a caller sizing a working set
against the level it will actually spill into has nothing to read.
The flat sysctl this crate would naturally have used is wrong in the same way
hw.l1dcachesize was, and by a larger factor. On a 12P/4E Apple silicon host
hw.l2cachesize reports 4194304, the efficiency cluster's cache, against the
performance cluster's 16777216. Adding L2 before core classes existed would
have reproduced the original defect at four times the size instead of two.
Reporting a capacity alone would also mislead. L2 is rarely private: that same
host shares 16 MiB across six performance cores and 4 MiB across four
efficiency cores, so neither figure is a per-core budget.
Solution: Report L2 capacity and how many cores share it, per class
L2_Cache_Size and L2_Sharing_Cores take the same core class as the L1
queries. A sharing count of one means the cache is private to a core; a
larger count means the reported capacity is divided among that many cores.
The GNATdoc on each function points at the other, because reading either
alone invites the per-core mistake.
macOS reads hw.perflevelN.l2cachesize and hw.perflevelN.cpusperl2. Linux
reads the level 2 entry of each CPU's cache description, counting the CPUs
its shared_cpu_list names. Both counts arrive as logical CPUs and become core
counts through the class's own threads-per-core ratio, so a host with two
threads per core does not report twice the cores sharing a cache.
Parsing a CPU list needs more than its leading index, so the Linux body gains
List_Count, which sums the single indexes and inclusive ranges a list names.
Describing L2 costs no extra file reads: the level 2 entry is found in the
same pass over the cache indexes that already locates the level 1 data entry.
The website cachelines guide gains the same pairing, with both names linked
to their generated entries.
Checks run: the crate suite on all three supported targets. A 12P/4E macOS
host reports 16777216 bytes across 6 cores for its performance class and
4194304 across 4 for its efficiency class, and the macOS test asserts that
these come from the performance level rather than the flat name. An aarch64
Linux host reports 16777216 across 6 cores. An x86-64 Linux host with 24
cores and two threads per core reports 2097152 across 1 core, where sysfs
gives 2048K shared by CPUs "0,24"; that run also compiles the CPUID body,
which builds nowhere else. Fixtures cover a per-class shared L2, an L2 shared
across SMT siblings, and a private L2. GNATdoc builds with no warnings.
9782c81b → updated to 7be049fcindex 6e2db0ef1 source commit
7be049fcThe two OpenSSL install steps in the test job carried no bound, so a mirror
that stops answering leaves apt waiting rather than returning an error. On
2026-08-19 run 32297071503, job 96211136305 sat on "Install OpenSSL 3 on
Linux" for 32 minutes and had to be cancelled by hand; it was the only check
blocking PR #49. Its log shows apt-get update fetching noble-security
InRelease at 20:13:04 and producing no further output until cancellation. The
macOS half of the same job passed, so nothing was wrong with the tests.
Left alone the job would have run to GitHub's six-hour default. Nothing else
in either workflow was bounded either: the checkout, the Alire install, the
index configuration, the Java and TLA+ downloads, and the GNATdoc install all
reach the network under the same default, and no job in this repository
declared a timeout at all, unlike the sibling repositories.
Solution: Bound every job and every step that reaches the network
Each job now declares a timeout-minutes, and so does each step that fetches
something. The bounds are generous multiples of what healthy runs measure
over the last 60 successful runs of each workflow, so they can only fire on a
stall. Jobs: test observes at most 17 minutes on macOS and 11 on Linux and is
bounded at 60; proof observes 6 minutes and TLA+ 7, both bounded at 30; the
pages build observes 69 seconds and is bounded at 20, its deploy 10 seconds
and bounded at 15. Steps: everything network-touching is bounded at 10
minutes against observed maxima of 1 to 6 seconds, except the Alire install,
which downloads the toolchain, observes 100 seconds, and is bounded at 15.
A step timeout alone would only have converted the 32-minute hang into a
10-minute one, because the stall is inside a single apt-get update that never
returns. That command is now capped per attempt with timeout(1) and retried
twice with 15 and 30 second backoff, so one unresponsive mirror costs a
retry rather than the job. The install that follows is capped as well but not
retried: a package that cannot be resolved should fail on the first try. The
worst case is three 120-second attempts plus a 10-second kill grace, 45
seconds of backoff, and a 190-second install, which is 10m25s and lands
inside the 12-minute step bound, so the annotation naming the transient
mirror is what the log ends on rather than a bare cancellation.
GNU timeout is used directly rather than scripts/run-with-timeout.sh. That
helper exists for portability to macOS, and this step is Linux-only.
The TLA+ download gains --connect-timeout 30 and --max-time 120 alongside its
existing --retry 3, because curl retries a failed transfer but not a stalled
one, and --max-time resets per attempt. Four bounded attempts stay inside the
step's 10 minutes.
Build, test, proof, and model-checking steps keep no step bound. Their
duration is workload-dependent, a bound sized for them would be a performance
budget rather than a stall detector, and the job timeout already covers them.
Nothing about what is installed, or how anything is built or tested, changes.
Checks run: both workflows parse. The retry script was extracted from the
committed YAML and driven under bash -e against stub apt-get invocations for
five outcomes: first-try success runs one update and one install; two
failures then success runs three updates and installs; three failures exit 1
with the annotation and never install; an install failure propagates its exit
status without retrying; and exit 124, which is what timeout(1) reports when
it kills a stalled command, is treated as retryable. The real commands were
then run in an ubuntu:24.04 container on coreutils 9.4, the runner's version:
the bounded update succeeds, interrupting one mid-flight reports 124, a
retry after that kill still succeeds rather than inheriting a poisoned apt
state, and the bounded install still yields OpenSSL 3.0.13 through pkg-config.
The convention is recorded in AGENTS.md so it holds for new steps, and it
matches the job-level timeouts already used in flyology-http and
gnat-patches, whose install steps want the same treatment.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7be049fc → updated to a53da35dindex b55699621 source commit
a53da35dThe allocator crate is developed alongside the runtime in this repository and
released on the same development line, but the dependency on it is declared as
"*". Nothing stops a dependent from resolving an allocator release built for a
different line, and the index has been carrying a tighter constraint by hand to
compensate.
Solution: Constrain allocators to its development line
Declare the line the allocator crate actually publishes, so the constraint
travels with the manifest instead of being reapplied wherever it is indexed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.1.1-devHTTP client and server library for Flyology tasks
ddbba90d → updated to d99490cfindex 510cfcf63 source commits
1802f1c9SPARK proof regressions and Autobahn conformance failures can go unnoticed because CI does not execute those campaigns. The existing nightly schedule covers other qualifications but not these policy and protocol checks.
Solution: Gate proofs and schedule Autobahn qualification
Run the complete QUIC and HTTP proof suite for every CI trigger and add a scheduled matrix for all maintained Autobahn profiles and task lanes, including matched OpenSSL provenance for secure profiles.
3d0df29fThe pull request proof job twice exhausts its level-one prover budget on QUIC ACK range obligations, even though the same obligations pass locally. This leaves an otherwise healthy CI run red and makes proof coverage unreliable on hosted runners.
Solution: Give CI level-two proof effort
Let both proof scripts accept FLYOLOGY_PROOF_LEVEL while preserving level one as the default for manual runs. Set the CI proof job to level two so hosted provers have enough effort to complete the full QUIC and HTTP campaigns.
d99490cfThe benchmark runner assumes Flyology runtime scripts live in the HTTP project root. Separate Codex worktrees and Alire dependency layouts do not provide that path, so runtime preparation fails before the benchmark can build.
Solution: Prepare the runtime from the resolved Flyology root
Resolve Flyology through the shared dependency-root helper and use that root when invoking prepare-rts.sh, preserving explicit, adjacent monorepo, and Alire-provided dependency layouts.
d99490cf → updated to d99490cfindex 28a2302dd99490cf → updated to 439a9fb6index 6e2db0ef30 source commits
24c1ea29Servers must currently mutate route setup state directly and cannot safely publish coordinated handler, policy, and middleware changes while requests are in flight. Callers also lack stable identities for addressing registered routes and middleware.
Solution: Publish immutable router generations through stable identities
Add opaque route and middleware IDs, transactional update operations, stale-writer detection, and single-load atomic generation publication. Exercise replacement and concurrent dispatch behavior, document the runtime and memory tradeoffs, and add the workflow to the server guide.
5e5aae59Every dispatch loads the published generation and then follows a second pointer to reach its immutable configuration. Each successful update also allocates a separate generation holder solely for retention bookkeeping.
Solution: Publish configuration allocations directly
Link retained configurations through their own private history field and atomically publish the configuration address. This removes the holder allocation and one dependent request-path load without changing stale-update or lifetime behavior.
1df8c3a6The retention chain, the publication gate, and the update base all carry
System.Address, so recovering a configuration needs an unchecked conversion
from an address-conversion pointer into a pool-specific access type. Router
initialization also allocates its first configuration in the declarative
part, where the handler written to release a failed allocation cannot see
it: an exception raised there propagates without reaching the handler.
Solution: Link retained configurations through a typed access
Complete the configuration type from an incomplete declaration and make its
access type general, so Previous, First_Configuration, the update base, and
the publication gate all hold access values and the unchecked conversion
disappears. The address round trip now stays on the published atomic word
alone. Allocate the first configuration in the statement part so its handler
covers the allocation it was written for.
38b98060Add, Add_Middleware, Add_Route_Middleware, Mount, and the two direct setters
write the currently published configuration in place, and they admit the new
element before they write it: the count is incremented first. A request that
races such a registration reads the raised count and then a route with a null
handler or a middleware entry with a null component, so the pipeline raises
Constraint_Error. Only the two setters carried a setup-only note, and the
identity-returning registration overloads carried none.
Solution: Seal the router at its first dispatch
Publish a seal word from the first dispatch and refuse every direct
registration and setter afterwards with Route_Error naming the update path.
The dispatch cost is one acquire load of a clean shared line, since the store
happens only while the word is still clear. Fill each route and middleware
slot before the count admits it so the ordering is sound on its own, and
state the sealing rule on the router type and the affected operations.
1fd5ed9bMount assigns a fresh identity to every middleware entry it copies into the
mounted routes, so the identity the application holds for a subrouter's
component matches nothing in the destination. Remove_Middleware and
Replace_Middleware then raise while the mounted copies keep running the old
component, even though both already walk every route chain accumulating
matches, which only makes sense when one identity can appear in several
chains. Begin_Update also accepted a mounted generation, and two candidate
setters skipped the mounted guard the other mutators apply.
Solution: Carry the source registration identity through a mount
Copy mounted middleware entries verbatim so one identity names every chain
that carries the registration. Refuse Begin_Update on a mounted generation,
which covers every candidate mutator in one place, and add the guard to the
admission and challenge setters that lacked it. State the mounting rule on
the identity type, on Remove_Middleware, and in the server guide.
8244e2fbCommit raises Stale_Update while the candidate is still attached, so
Begin_Update on that object then raises Program_Error. The documented retry
loop only works because it declares the update inside the loop; hoisting it,
which is the obvious way to avoid re-entering the declaration, deadlocks the
caller into an unusable object. There is also no way to give up on a
candidate that failed validation.
The concurrency audit could not fail cleanly either. Its workers park on a
barrier that a failure before Start never opens, and the join waits for all
four to finish with no bound, so a setup error hangs the audit until the
harness timeout instead of reporting. Its assertion also could not tell a
run that spanned a publication from one that never observed a change.
Solution: Release a stale candidate and bound the audit
Release the candidate when Commit rejects it as stale, since its base is gone
and it can never be published, and leave a validation failure active so the
caller can correct and commit it. Add Abandon for an explicit discard.
Rebuild the audit around a cancellable barrier, bounded joins, and a fixed
publication count chosen odd so the closing request of every worker has one
correct answer. Workers now record a pre-publication and a post-publication
observation, so the test proves it spanned a generation change. Cover the
duplicate-name and ambiguity rejections that candidate Add defers to Commit,
and the Set_Match, Rename, Set_Middleware_Stage, and admission operations
that had no coverage at all.
5e9b0b41Every introspection operation takes its own acquire load, so a caller that
reads Route_Count and then describes each index reads two generations. A
commit that removes a route between the two calls makes the last index
invalid and Describe_Route raises Constraint_Error. The same split affects
Route_Middleware_Count with Describe_Route_Middleware. A diagnostics endpoint
enumerating routes therefore fails intermittently on exactly the deployments
runtime reconfiguration is for, and nothing lets a caller pin a generation.
Solution: Add a snapshot that pins one generation
Add a Snapshot that captures the published generation, with the whole
introspection surface available on it. Route the Router forms and the
Snapshot forms through shared configuration-level operations so both answer
identically. Retained generations already outlive any snapshot, so the
snapshot only holds a reference; its finalization is the hook a later
reclamation scheme needs.
b345dc6fCommit links every superseded configuration into the router's retention
chain, and only router finalization frees it. Retention is what keeps an
in-flight dispatch from reading freed storage, but nothing bounds it and
nothing reports it: a control plane that commits periodically grows the
chain for the whole process lifetime with no way to observe the growth and
no way to release it short of destroying the router.
Solution: Report retained generations and release them on demand
Add Retained_Generations so the growth is measurable, and Reclaim to release
every superseded generation while keeping the published one. Reclaim states
its precondition plainly: the caller must know that no dispatch is running
and no snapshot of a superseded generation is live, and that is not checked.
This keeps the request path free of reader accounting while giving a drained
server a way to return the memory.
e0a9e1b8Registration compiles each pattern into a Segment_List so dispatch does not
split it per request, but the ambiguity checks throw that away. Patterns
Overlap and Specificity both take a String and split it again, so Check_Add
re-splits every stored pattern on every registration and Commit re-splits
both sides of every route pair. Add also computed the same split twice, once
for storage and once for the score. The name-keyed Add_Route_Middleware
scanned for the name, took an identity, then re-read the router and scanned
again for that identity, which can report an identity failure for a name
that does resolve. Name comparisons allocated a String per route inspected.
Solution: Compare the segments and score each route already carries
Take a Segment_List in Patterns_Overlap and Specificity, so both the
registration check and the commit check read the stored segments and the
stored score. Order the cheap score comparison before the segment walk.
Compile each pattern once per Add. Resolve the name-keyed middleware
overload in one pass over one generation. Compare names against the stored
Unbounded_String instead of converting each one.
Cover the result with an oracle: for each of eight pattern pairs, assert the
registration decision and the commit decision are both the expected verdict.
bdcfbc8cThe guide still tells applications to bound their update rate when retained
memory matters, which describes a rate tradeoff rather than growth the
router now reports and can release. It states that direct registration must
not run concurrently with dispatch without saying the router enforces that.
It describes per-call introspection as a snapshot, which is what the new
Snapshot type actually provides, and it tells a caller to rebuild a stale
candidate without saying the candidate is already released.
Solution: Describe sealing, snapshots, and generation retention
Replace the retention paragraph with Retained_Generations and Reclaim, and
state Reclaim's unchecked precondition beside the call. Say that the first
dispatch seals the router and what a later direct registration raises. Add a
short section for traversing one generation with Take_Snapshot, and record
that Stale_Update releases its candidate while a validation failure keeps
one for correction. Every new API mention links its generated GNATdoc entry,
and all 286 generated-API links on the hand-written pages resolve.
799995fdRouter_Configuration is a large non-limited discriminated record, so RM 6.2
leaves by-copy or by-reference to the implementation for an in parameter.
Dispatch_Configuration takes a generation that way, and so does every
introspection and validation operation that reads one. GNAT passes them by
reference and the request-path measurements show no copy, but nothing in the
language requires it. An implementation that chose by-copy would duplicate a
whole generation, with every route pattern, segment list, and middleware
name, on each request.
Solution: Take every generation parameter explicitly aliased
An explicitly aliased parameter is passed by reference by definition, so
mark all fourteen Router_Configuration parameters aliased. Every actual is
already an aliased view reached through a Configuration_Access, so no call
site changes. The guarantee is now a property of the declaration rather than
of one compiler's choice.
977add4aThe published-generation word a dispatch reads and the word a commit writes
live in the same router object, so a reload can invalidate the line the
dispatchers are reading. Nothing measures that. The existing benchmark
servers run over sockets and an external load generator, where connection
and syscall cost hides an effect this small, and none of them reload the
router while traffic runs.
Solution: Add an in-process router reload benchmark
Drive routed dispatch from worker tasks over an allocation-free in-memory
transport, with one loader task republishing the configuration at a chosen
rate, and sweep that rate against otherwise identical traffic. Each worker
counts on its own stack and reports once at the end, so the harness adds no
contention of its own to the thing being measured.
The runner repeats each rate as independent process runs and reports best,
median, worst, and spread, because code layout changes per link and process
placement dominates repetition inside one run. Reporting the spread is the
point: an effect smaller than it cannot be claimed from this harness.
926fed4bEvery dispatch reads the published-generation word and the seal word. Both
sit in the same record as First_Configuration and the publication gate,
which only a commit touches, and nothing in the declaration separates the
two groups. Whatever layout the compiler happens to produce today is a
consequence of field order and padding, not a property anyone stated.
Solution: Place the two dispatch-read words in one interference region
Group them with Flyology_Cachelines.Padded_Groups, so a dispatch fetches one
region rather than two and a commit dirties the readers' region rather than
whatever else landed beside it. The two words have the compatible ownership
that a group requires: each is read by every dispatch and written only by a
commit.
This is not a measured improvement and must not be read as one. A commit
dirties the readers' region once, not once per dispatch, so the cost follows
the reload rate rather than the traffic: between 10 and 100 reloads per
second it is 0.0004% to 0.004% of throughput, four orders of magnitude below
what the reload benchmark can separate from run-to-run spread. The change is
made because the layout is then correct by declaration instead of by
accident. The benchmark now records what it can and cannot resolve, so the
question is not reopened on the assumption that a measurement is available.
8cae2c89The HTTP/2 qualification job installs its independent peer with a bare
sudo apt-get update, and no step in either workflow bounds its own runtime.
When the mirror stops answering, apt-get does not fail; it waits. The step
waits with it, and the only thing that eventually ends the run is the job
timeout, or a person noticing.
On 2026-08-19 that happened three times. Run 32226557711 on main sat on the
update for the whole 150-minute job budget before the job timeout ended it,
with nobody watching. Runs 32278682887 and 32294697524 sat there 83 and 64
minutes before someone cancelled them by hand. The same job finishes in 8 to
14 minutes when the mirror answers, and the install itself takes 11 seconds
at the median, so each of those runs spent between four and eleven times the
job's whole healthy duration waiting on one command. A run that hangs also
reads as a run that is working: there is no failure to retry, and the
concurrency group keeps the branch occupied until someone intervenes.
Solution: Bound every network step, and retry the update that stalls
Give each step that reaches the network a timeout-minutes bound, sized
against the slowest healthy observation over the preceding 60 runs rather
than against a guess, so a step that trips one has stalled rather than run
slowly. Checking out sources and configuring the index finish in seconds and
are given 5 minutes; the peer install runs 11 seconds at the median and 5
minutes at its slowest and is given 15. Installing Alire is given 15 on
different grounds: every observation of it is a cache hit, so the
observations bound the cached path and say nothing about a cold toolchain
download.
Wrap apt-get update in three attempts, each bounded on its own by timeout,
because the step bound alone would convert a stall into a failure but not
into a recovery. A wedged mirror connection now becomes a retry against a
fresh one, so a single transient failure does not fail the job, and the step
bound stays a backstop rather than the mechanism that catches the common
case. Only the update is retried: it is the command that stalled, and it is
the one here that is safe to interrupt and repeat, since a killed run leaves
partial package lists that the next run refetches. Retrying apt-get install
would risk resuming from a half-configured dpkg state to no purpose.
The same failure mode existed in two other places and is closed the same
way. The Pages workflow had no job-level timeout at all, so a stall there ran
to the six-hour GitHub default; both its jobs now carry one, and its fetch
and install steps carry step bounds. The h3spec download in the HTTP/3
conformance script used a bare curl -fL, which hangs on a stalled connection
exactly as apt-get did; it now retries with a per-attempt time limit, which
the existing checksum makes safe, because a truncated or resumed body never
survives it.
Nothing about what is installed or how anything is built or tested changes.
On a healthy run every bound is untouched and every command runs as before;
the bounds only decide how a stalled run ends.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
27a8ee59flyology_iri_benchmark times can_parse and parse_href over the pinned URL corpus. Neither reaches a component getter: can_parse constructs nothing, and parse_href consumes only the stored serialization length. The paths that read components back out of an already parsed reference, and Resolve above them, have no benchmark at all, so a change to them can be argued only from reading the code.
Solution: Add a flyology_bench harness for those paths
flyology_iri_resolve_benchmark reads all eight components at three reference lengths and resolves the RFC 3986 section 5.4 references against a short and a long base. It carries its own inputs, so unlike benchmark.sh it needs neither the Ada URL checkout nor the pinned corpus. Batches fold a value derived from each result into an accumulator handed to a barrier after the ending timestamp, and each iteration advances to a different input.
Two builds of one library cannot share a process, so before/after runs join through Flyology_Bench.Baselines, whose fingerprint refuses a comparison across operating system, architecture, compiler, or build mode while deliberately excluding the library revision. scripts/resolve-benchmark.sh selects the release mode the published medians measure and passes it to the binary for that fingerprint. Diagnose and Parse do coexist, so the validation pair is a real paired Compare.
Requested axes are Process_Resource_Metrics, the portable Darwin/Linux set; the metrics CSV reports each axis's collection status rather than leaving it assumed.
d45630c0Every public component getter reaches the stored serialization through the private Slice helper, which materialized the entire reference with To_String and then returned one range of it. The release objects show that call sited unconditionally and ahead of the zero-span test, so an absent component paid a whole-reference copy and a present one paid two allocations and two copies. Reading a four-character scheme out of a 233-byte IRI copied 233 bytes and then four. Resolve sits directly on top of that and reads Path four times and the base path twice. Remove_Dot_Segments, which runs on essentially every resolution, renormalized its input to one-based indexing through an Unbounded round trip.
Solution: Copy only the component, and read each path once
Slice now calls Ada.Strings.Unbounded.Slice, keeping the zero guard ahead of it because an absent component has First = 0 and that subprogram takes a Positive Low. The getters compile to a tail call to that subprogram on the present path and to an eight-byte bounds allocation on the absent one. The returned bounds do not change: GNAT's Slice returns the underlying Low .. High range, which is what the discarded whole-text copy was sliced by. Remove_Dot_Segments renormalizes with a constrained subtype instead of the heap. Resolve hoists the relative path and the base path into locals.
Release-mode medians on an Apple M3 Max, interleaved before/after/before/after against an unrelated build holding the load average near 25, with the repeated pre-change round as a drift control: reading all eight components falls 41.9% at 44 bytes, 33.5% at 235, and 63.8% at 2,009; resolution falls 22.2% against an 18-byte base and 15.5% against a 233-byte one. The drift control landed within 3% of its own first round. Process_RSS_Change and both page-fault axes read zero on both sides, as expected for churn the allocator reuses within a batch; wall time and thread CPU time are the axes that respond.
Component tests now name the absent components, since the zero guard is what keeps an absent span away from a Positive parameter.
4df3c149Resolve ends by parsing its assembled result into a second Reference, a heap serialization plus eight component spans, purely to establish that the bytes are well formed. A caller that stores resolved references as opaque bytes needs exactly that guarantee and none of the spans, and has no way to ask for it.
Solution: Add a serialization-returning Resolve
Diagnose establishes the same guarantee without constructing anything, and for URI and IRI it is the same computation Parse performs: both reduce to Analyze's error, and a successful non-web parse stores its input verbatim. Web mode normalizes, so there the normalized serialization is the result and the new form still parses to obtain it. The value equals Image of the Reference the other form returns, which the section 5.4 suite now asserts on every case it already covered.
The two forms share a private Assemble rather than one calling the other. Composing them the obvious way, with the Reference form parsing the serialization form's result, would make it validate twice and cost it roughly 210 ns it does not pay today.
Paired Compare in one process, release mode, against a 233-byte base: 821.3 ns/op returning a Reference against 743.4 ns/op returning a String, 9.45% less, 95% CI [1.102, 1.107], winning 100 of 100 sample pairs at an order effect of -0.46%. Isolating the validation step, Diagnose measures 211.2 ns/op against Parse's 270.1. The end-to-end saving exceeds that difference because the serialization form also skips constructing and finalizing the result Reference, not only the parse that fills it. The Reference form's median is unchanged, which is what confirms it kept its single validation.
cfb25efcResolve merges and reduces its result by appending to an Unbounded_String, which reallocates on nearly every append, and by calling Remove_Dot_Segments, which builds a second one and returns To_String of it. Segment removal is worse than that suggests: collapsing one "/../" runs a whole To_String followed by a Set_Unbounded_String. None of this is visible in the API, and all of it is on the path of every resolution.
Solution: Assemble into a String sized from the parsed references
Merging and dot-segment removal never produce more than the base, the relative reference, and the separators between them, so the serialized lengths of the two parsed references bound the result. Both are real lengths rather than Max_Length, which may be Positive'Last, and both come from parsed references rather than input bytes, because web normalization can expand a relative past what it was written with. A bound above what belongs on the stack takes one exact allocation instead, which keeps a single copy of the merge rather than a rarely exercised second implementation.
Release-mode paired medians against a 233-byte base: the Reference form falls from 823.2 to 550.4 ns and the serialization form from 743.4 to 466.5, so the change reaches the API every existing caller already uses. Resolution against an 18-byte base falls from 475.8 to 331.0. The public specification is untouched.
A procedure form writing into a caller-supplied buffer was prototyped against this and measured 4.7% below the serialization form, winning 69 of 100 pairs. That difference is the returned copy alone, which is all the wider API buys once the assembly no longer allocates, so it is not adopted.
616fdfd5Assembling into a buffer put four objects proportional to the reference length on the primary stack: the one-based copy of the dot-segment input, the merged path, the prefix taken out of the base path, and the result staged on the way out of the heap branch. The heap branch existed precisely so that a caller raising Max_Length would not put a large buffer on the stack, and then reached the stack anyway through the other three. Resolving a four megabyte base raised Storage_Error where the previous implementation returned a result. The heap branch also freed its buffer only on its two normal exits, so any exception through it leaked the allocation, and neither the bound nor the append guard was safe against wraparound in a body whose release profile suppresses overflow checks.
Solution: Keep every size-proportional object off the primary stack
Dot-segment reduction indexes its input through an offset rather than copying it one-based, so the copy is gone rather than moved. The prefix is named by bounds into the base path and written straight into the merged buffer. The merged path takes the same stack-or-heap fork as the assembly buffer, because section 5.2.4 reduces the joined path as one string and the pieces cannot be reduced separately. An extended return builds the result where the caller wants it. Both heap blocks free their allocation from an others handler as well as their normal exits.
The append guard subtracts rather than adds, so it cannot wrap, and the bound is computed in Long_Long_Integer and rejected unless it fits Natural with room to spare, which also keeps every later index lookahead inside range.
Resolving against bases of one hundred kilobytes, four megabytes and forty megabytes now all succeed, matching the previous implementation. A differential harness comparing both public forms against a verbatim reimplementation of the previous assembly reports no divergence over 429,208 cases, of which 105,202 raise on both sides.
18ecffdbResolve returning String and Resolve returning Reference differ only in what they return. Most calls select between them from context, but a call whose context supplies no type does not: comparing two resolutions with "=", or taking an attribute of one, becomes ambiguous. Both were legal while only the Reference form existed, so an existing caller of the published crate meets a compiler error whose cause is not apparent from the code in front of it.
Solution: Say so where the operation is documented
Record the overload, name the two shapes that stop compiling, and give the disambiguation. A qualified expression settles either in place, and naming the result in a constant always works. Keeping the shared name is deliberate: the failure is a compile error rather than a silent change of meaning, and the fix is local to the call.
2f3df5f8BENCHMARKS.md carries medians taken before the assembly stopped staging size-proportional locals on the stack, and its comparison between the two assemblies stitches together runs from separate sittings on a host under varying load.
Solution: Re-measure both assemblies in one interleaved sitting
Swap the two assemblies in place across four rounds, first arm repeated as a drift control, and report the resolution rows from that. The control landed within 0.4% of itself. Removing the one-based copy from dot-segment reduction moved the figures slightly in its favour, so the buffer assembly now reads 34% below the Unbounded one for both public forms rather than 33%.
4b7b1d6cCan_Parse and Diagnose report whether input parses but not how it
classifies, so a caller that admits only absolute references -- an RDF
crate deciding whether bytes are an absolute IRI, say -- has to reach for
Try_Parse. Try_Parse builds a Reference, and building one stores a copy
of a serialization that is byte for byte the input the caller already
holds. The caller reads Kind and drops it.
Solution: Report the classification without building the reference
Add Inspect, which runs the analysis Try_Parse runs and reports the
structural kind alongside the parse error. Analyze works on spans over
the caller's String, so nothing is stored and nothing is allocated. The
differential harness now holds Inspect to Try_Parse on both the error and
the kind across the whole seeded corpus.
Admitting "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" as an
absolute IRI:
allocs bytes ns
via Try_Parse 1 80 145
via Inspect 0 0 103
-100% -100% -29%
Measured with FLYOLOGY_IRI_BUILD_MODE=release. Both forms were run
alternately, eight rounds each, and the minimum taken, because the host
carries unrelated build load. Allocation counts are exact.
bc3e3217Preprocess removes tab, newline and carriage return by appending the
surviving bytes to an Unbounded_String one at a time. Almost no URL holds
any of them, so the ordinary case takes an accumulator, grows it, and
returns a verbatim copy of its own input.
Solution: Return the trimmed slice when there is nothing to remove
Scan first and build only once a byte has to be dropped, which also keeps
the controlled accumulator out of the common path entirely. The slice is
slid to one because Input_Offset and the parser below read the result at
one-based offsets.
Absolute web URLs that take the WHATWG pipeline rather than the fast
path:
allocs bytes ns
web_slow, before 25 1008 2072
web_slow, after 23 912 1964
-8% -10% -5%
web_idna, before 16 752 1661
web_idna, after 14 656 1547
-13% -13% -7%
Measured with FLYOLOGY_IRI_BUILD_MODE=release. Revisions were built into
separate worktrees and run alternately, eight rounds each, minimum taken,
because the host carries unrelated build load. Allocation counts are
exact.
WPT URL conformance stays at 919/919 with no mismatches.
bf79ef49Encode, Encode_Opaque_Path and Percent_Decode each accumulate into an
Unbounded_String one character at a time and then copy the whole thing
out with To_String. That is a heap block, the reallocations that growing
it byte by byte implies, and one full copy, for a result whose length is
a pure function of the input. Two of the callers encode a single
character at a time inside a loop, so each iteration paid all of it.
Solution: Count the result, then write it into the returned object
Each function now measures its output in a first pass and fills an
extended return object in a second. Since the count is exact, the
returned String is the only storage involved -- no accumulator and no
working buffer, so nothing is allocated and nothing is copied.
Sizing a buffer up front was the alternative, but the bound that is known
a priori is three bytes per input byte, and with Max_Length at one
mebibyte that reserves three mebibytes on the stack in a crate that does
not build with -fstack-check. Counting first costs a second pass over the
input and reserves exactly what the result needs.
Absolute web URLs that take the WHATWG pipeline rather than the fast
path:
allocs bytes ns
web_slow, before 23 912 1964
web_slow, after 17 720 1670
-26% -21% -15%
web_idna, before 14 656 1547
web_idna, after 12 592 1449
-14% -10% -6%
Measured with FLYOLOGY_IRI_BUILD_MODE=release. Revisions were built into
separate worktrees and run alternately, eight rounds each, minimum taken,
because the host carries unrelated build load. Allocation counts are
exact and do not depend on any of that.
The corpus benchmark does not move: over the pinned 100,025 URLs,
can_parse holds at 22 ns minimum and parse_href at 105 against 103, with
acceptance identical at 99999. That corpus is dominated by URLs taking
the allocation-free fast path, which never reach either function.
WPT URL conformance stays at 919/919 with no mismatches, which is the
corpus that exercises these two functions hardest.
82106435Since "The router's dispatch-read words share a region with writer state",
routing.ads groups its two dispatch-read words with
Flyology_Cachelines.Padded_Groups. That spec is one of the files the
documentation build documents, and GNATdoc walks every project in the
closure that Documentation.Excluded_Project_Files does not name, so the
private with dragged the cachelines crate in with it. Its two tagged
types, Fitted_Groups.Grouped_Array and Padded_Groups.Grouped_Array, then
appeared in the index's tagged-type section, each linked to a page of its
own -- and GNATdoc writes a page per tagged type only under
--html-oop-style, which this build does not pass. This crate's own tagged
types are linked as unit page plus fragment and resolve; those two never
could.
The site check reports both as missing targets, so the Pages workflow has
failed on every push to main since that commit landed and the published
site is frozen at the last green deploy. CI is unaffected: the two
workflows fail independently, and only one of them was ever red.
Solution: Document this crate and nothing else
Name every dependency in Excluded_Project_Files, beside the Flyology
runtime that was already there. Excluding cachelines alone would clear the
two broken links, but the closure walk is why they were reachable at all,
and the same walk had already put twenty-four pages of QUIC and allocator
internals into a reference whose own footer reads "Public HTTP API only".
The index now lists this crate's sixty-two entries, the search index
carries 2441 names with none from a dependency, and no page outside
Flyology.HTTP is written.
Removing those pages breaks no link. Their only inbound references were
the index entries that go away with them: scanning every website page and
every generated page for their file names finds nothing else pointing at
them.
Each exclusion names its project through the prefix Alire exports for that
crate rather than a path under FLYOLOGY_ROOT. The Flyology crate vendors
copies of the cachelines and allocator sources, so the two resolve to
different build directories, and GNATdoc rejects an excluded file that is
not the project it loaded -- "unable to resolve project file path", a hard
error rather than a silent no-op, so naming the vendored copy fails the
run outright. A bare project name fails the same way.
Verified with docs/api deleted first, since the workflow builds from a
fresh checkout and a stale page left behind by an earlier run would hide a
link the workflow cannot resolve. scripts/build-site.sh, which is what the
workflow runs, reports "Site check passed: 70 HTML files and 136 total
files".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
83db8c4dThe dual-stack overload of Routing.Serve divides HTTP_3_Capacity between the
address families, so HTTP_3_Capacity => 8 gave each listener four connection
slots rather than eight. Both client profiles in this test address
"localhost", which resolves to every loopback family on some hosts, and the
client's connection race completes a full QUIC handshake on each family
before it discards the losing lane. Each family therefore admits up to five
concurrent connections here: two live client profiles, two losing race lanes
awaiting release, and the raw Initial. Instrumented runs reach four of four
slots routinely.
A listener whose registry is full discards an Initial without answering it,
and no handshake-space probe timeout retransmits one. The raw handshake loop
compounded that: its receive raised Flyology.IO.Timeout_Error straight out of
the loop, so the eight attempts were never a retry budget and the first quiet
interval ended the run. The response loop had the same shape. Sizing a run of
twenty at each capacity reproduced the failure in nineteen runs at two slots
per family, seven at three, and one at four, tracking the discarded Initials
one for one.
Solution: Retransmit the handshake flight and size the listener for the load
Retain the last flight sent to the server and resend it when a receive
expires, spending the attempt budget on eight two-second probes rather than
one ten-second wait. The response loop keeps its budget for waiting, because
the server does drive application-space probe timeouts and retransmits on its
own; sixteen one-second probes stay inside the server's connection age.
Neither loop now ends an exchange on a single discarded datagram.
Raise HTTP_3_Capacity to sixteen and record the actual per-family model in
the comment, so the test stops running its listeners at full occupancy by
accident. With the retransmission in place the capacity is no longer load
bearing: at two slots per family, where the previous loop failed nineteen
runs of twenty, the raw handshake now completes in all twenty.
This is a test change. The absent handshake-space probe timeout is a real gap
in the QUIC layer and is not addressed here.
d7d87344CONNECTION_CLOSE is only reachable through the application space. An endpoint
that abandons a connection while it still holds Initial or Handshake keys has
no way to say so, and Build_Application_Close_Datagram's precondition rules it
out.
The receiving side is missing too. Initial_Space and Handshake_Space parse a
peer Transport_Close frame, count it, and discard it. A peer that did somehow
send one would be ignored, so adding a builder alone would change nothing
observable.
Solution: Report a peer close from both spaces and expose a close builder
Initial_Space and Handshake_Space now carry Peer_Closed and Close_Error in
their Process_Result and stop parsing at the frame, since RFC 9000 10.2.2 puts
the receiver into the draining state and the rest of the packet cannot change
that. Connection_Driver acts on it exactly as it already does for the
application space: Peer_Closed state, Connection_Closed status.
Build_Handshake_Close_Datagram builds the outbound half in the highest space
the connection holds, Handshake once that space exists and Initial otherwise,
so a peer that reached either level can read it. It carries the transport
APPLICATION_ERROR code that RFC 9000 10.2.3 reserves for an application close
that its encryption level cannot express, and leaves the connection Failed
because an endpoint that has begun closing must send nothing else.
Initial_Space.Build_Transport_Close_Packet passed a zero minimum packet length
for both roles, so a client-sent Initial close fell under 1,200 bytes and RFC
9000 14.1 requires a server to discard it. It now derives the minimum from the
role, as Build_Crypto_Packet already did. That also corrects the existing
client Fail_Initial_With_Close path, whose closes were unreadable for the same
reason.
c041b36dFor HTTP/3 both lanes of the address-family race run Start_Connection, which
completes a full QUIC handshake before Race.Choose picks a winner. The loser
raises Connection_Race_Lost, Close_Candidate runs, and Dispose_Connection sends
an application CONNECTION_CLOSE only when the transport reached the connected
state. A lane cancelled mid-handshake never did, so the client closes its UDP
socket and says nothing.
The server is left waiting for a handshake that will never arrive. On a bounded
listener that costs a connection slot for the whole handshake timeout, and a
peer that repeatedly starts and abandons handshakes can hold every slot.
Solution: Send a handshake-space close from the abandoned lane
Dispose_Connection now picks the close by transport state rather than skipping
it: an application close when connected, and Build_Handshake_Close_Datagram
when the connection is still in Client_Initial or Client_Handshake. Both go out
on the connection's own UDP socket before it closes, and both remain best
effort, since a disposal path cannot report a send failure to anyone.
fe4a9d4dServe_Connection waits for QUIC.Is_Connected against one deadline, the absolute
handshake timeout measured from the connection epoch. A peer that sends an
Initial and then stops answering is indistinguishable from one that is merely
slow, so its worker waits out the full budget and its Connection_Registry slot
stays taken. A cooperative peer now sends a close, but a crashed, partitioned,
or hostile one still does not.
Solution: Deadline an unconnected peer on no progress as well as elapsed time
Connection_State records the arrival of each inbound datagram, and the
pre-handshake wait takes the earlier of the absolute handshake deadline and
that arrival plus Handshake_Idle_Timeout. The absolute budget still bounds a
peer that keeps sending; the no-progress budget reclaims the slot from one that
stops.
Default_Handshake_Idle_Timeout is three seconds. Handshake retransmission on a
lossy path arrives well inside that window and counts as progress, so only a
silent peer reaches it. Serve_Listener and both Serve overloads take an
override. Routing.Serve is left alone: it gets the shorter reclamation from the
default without adding a parameter to eight overloads.
439a9fb6Both reclamation paths, the peer's transport CONNECTION_CLOSE and the server's
no-progress deadline, are only observable as the absence of a stall. No test
saturates a bounded listener with half-open handshakes, so either could regress
into the ten-second wait it replaced without a failure anywhere.
Solution: Saturate a listener of capacity N and require a fresh Initial
http3_handshake_abandon runs two phases against a capacity-three listener whose
handshake timeout is twenty seconds, far beyond every deadline it asserts, so a
slot reclaimed inside one of them cannot be explained by that timeout. Each
phase fills every slot with a handshake that will not complete, asserts that a
further probe is refused, releases the lanes, and then requires a fresh Initial
to be admitted.
The first phase abandons with closes and leaves the no-progress deadline out of
range; the second goes silent and relies on the deadline alone. Odd lanes
consume the ServerHello first so they abandon from the Handshake space, while
even lanes abandon from the Initial space, covering both protection levels. The
probe repeats its Initial while no worker answers, the way a real client's probe
timeout would, so a slot released partway through the budget is still taken up.
Each phase was confirmed to fail on its own: removing the driver's peer-close
reporting fails the first, and dropping the no-progress term from the deadline
fails the second.
439a9fb6 → updated to dac7fa90index 377df96f2 source commits
a3866c57Flyology.QUIC.Connections exposes loss recovery only for the application
packet-number space. The Initial and Handshake spaces track nothing they
have sent, discard the peer's ACK frames after parsing them, and never
acknowledge an ack-eliciting Initial or Handshake packet, which RFC 9000
13.2.1 requires immediately. Nothing arms the RFC 9002 6.2 probe timeout
those spaces need.
Both HTTP/3 drivers inherit the gap. Start_Connection loops on Receive_One
until the transport connects and Serve_Connection waits up to
Handshake_Timeout for progress; neither retransmits its own flight. A single
dropped client Initial or dropped server flight therefore costs the caller's
whole deadline rather than one probe timeout, which is how an ordinary
transient loss became a hard CI failure in the raw handshake lane of
tests/http3_server_integration.adb. That lane now works around the gap by
resending its own flight; the gap itself is still open.
Solution: Give the Initial and Handshake spaces RFC 9002 loss recovery
Each space now owns a sent-packet ledger and retains the CRYPTO ranges it
has sent in Crypto_Flight, so a probe can rebuild a range under a fresh
packet number rather than resending a packet QUIC forbids repeating. Peer
ACK frames drive Sent_Packet_Policy.Apply_ACK and yield the delivery, loss,
and RTT events the spaces previously threw away. Every packet a space builds
carries the acknowledgment it owes, and a datagram that produces no response
sends an ACK-only packet, so a peer's probe timeout clears within one round
trip.
Connection_Driver holds one path-wide Recovery_Policy state for both spaces:
a single probe count backing off together, and a probe timeout that excludes
max_ack_delay because handshake packets are acknowledged immediately.
Has_Recovery_Timeout, Recovery_Deadline, and Process_Timeout now report the
earlier of the handshake and application deadlines, and an expired handshake
timeout retransmits every unacknowledged CRYPTO range. A client whose peer
has not completed address validation sends the 6.2.2.1 anti-deadlock probe
instead. Progress that can only follow delivery of a flight retires it
without waiting for an ACK.
The HTTP/3 drivers needed almost nothing. Both already bounded each receive
by Recovery_Deadline and processed an expired timer, so widening the timer to
cover the handshake spaces made their loops retransmit; Start_Client gained a
defaulted Now so the timer has an anchor.
The raw QUIC lane of the server integration test carried a stand-in for this
gap: it retained the last flight it sent and resent it when a receive expired,
because nothing below the test retransmitted. The transport owns that now, so
the lane drives Process_Timeout against its own clock and the retained flight
is gone. Its listener sizing and the patience of its response loop are
unchanged, since neither depended on the missing timeout.
Handshake-space congestion is accounted but never gates a send: the flight
is far below the initial window and RFC 9002 permits probing regardless.
Immediate acknowledgment costs one extra packet per silent datagram,
including a padded client Initial ACK when only the ServerHello arrives;
that is what lets a peer stop retransmitting after one round trip.
Two new tests cover the loss. A deterministic transport test drives an
explicit clock through a dropped client Initial, a dropped server Handshake
flight, and the anti-deadlock probe, checking the exact first deadline, the
doubled backoff, and that an already-acknowledged Initial flight is not
retransmitted. An end-to-end test puts the routed server and the standard
client either side of a UDP relay that swallows one flight; with the probe
timer disabled it stalls for the full deadline and fails.
dac7fa90The sibling checkout pinned for local development also stood in for the QUIC
dependency's version, leaving it declared as "*". Local builds resolve through
the pin and never notice, but the pin cannot cross into the index, so anything
depending on this crate is free to resolve any QUIC release at all.
Solution: Declare the version the pin stands in for
Constrain the dependency to the development line the pinned checkout provides,
so the same version is selected once the pin is gone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
flyology_cachelines ~0.1.1-devflyology_quic *→ changed to ~0.1.1-dev0.1.1-devAllocation-conscious URI, IRI, and URL reference parser
ddbba90d → updated to d99490cfindex 510cfcf63 source commits
1802f1c9SPARK proof regressions and Autobahn conformance failures can go unnoticed because CI does not execute those campaigns. The existing nightly schedule covers other qualifications but not these policy and protocol checks.
Solution: Gate proofs and schedule Autobahn qualification
Run the complete QUIC and HTTP proof suite for every CI trigger and add a scheduled matrix for all maintained Autobahn profiles and task lanes, including matched OpenSSL provenance for secure profiles.
3d0df29fThe pull request proof job twice exhausts its level-one prover budget on QUIC ACK range obligations, even though the same obligations pass locally. This leaves an otherwise healthy CI run red and makes proof coverage unreliable on hosted runners.
Solution: Give CI level-two proof effort
Let both proof scripts accept FLYOLOGY_PROOF_LEVEL while preserving level one as the default for manual runs. Set the CI proof job to level two so hosted provers have enough effort to complete the full QUIC and HTTP campaigns.
d99490cfThe benchmark runner assumes Flyology runtime scripts live in the HTTP project root. Separate Codex worktrees and Alire dependency layouts do not provide that path, so runtime preparation fails before the benchmark can build.
Solution: Prepare the runtime from the resolved Flyology root
Resolve Flyology through the shared dependency-root helper and use that root when invoking prepare-rts.sh, preserving explicit, adjacent monorepo, and Alire-provided dependency layouts.
d99490cf → updated to 439a9fb6index 6e2db0ef30 source commits
24c1ea29Servers must currently mutate route setup state directly and cannot safely publish coordinated handler, policy, and middleware changes while requests are in flight. Callers also lack stable identities for addressing registered routes and middleware.
Solution: Publish immutable router generations through stable identities
Add opaque route and middleware IDs, transactional update operations, stale-writer detection, and single-load atomic generation publication. Exercise replacement and concurrent dispatch behavior, document the runtime and memory tradeoffs, and add the workflow to the server guide.
5e5aae59Every dispatch loads the published generation and then follows a second pointer to reach its immutable configuration. Each successful update also allocates a separate generation holder solely for retention bookkeeping.
Solution: Publish configuration allocations directly
Link retained configurations through their own private history field and atomically publish the configuration address. This removes the holder allocation and one dependent request-path load without changing stale-update or lifetime behavior.
1df8c3a6The retention chain, the publication gate, and the update base all carry
System.Address, so recovering a configuration needs an unchecked conversion
from an address-conversion pointer into a pool-specific access type. Router
initialization also allocates its first configuration in the declarative
part, where the handler written to release a failed allocation cannot see
it: an exception raised there propagates without reaching the handler.
Solution: Link retained configurations through a typed access
Complete the configuration type from an incomplete declaration and make its
access type general, so Previous, First_Configuration, the update base, and
the publication gate all hold access values and the unchecked conversion
disappears. The address round trip now stays on the published atomic word
alone. Allocate the first configuration in the statement part so its handler
covers the allocation it was written for.
38b98060Add, Add_Middleware, Add_Route_Middleware, Mount, and the two direct setters
write the currently published configuration in place, and they admit the new
element before they write it: the count is incremented first. A request that
races such a registration reads the raised count and then a route with a null
handler or a middleware entry with a null component, so the pipeline raises
Constraint_Error. Only the two setters carried a setup-only note, and the
identity-returning registration overloads carried none.
Solution: Seal the router at its first dispatch
Publish a seal word from the first dispatch and refuse every direct
registration and setter afterwards with Route_Error naming the update path.
The dispatch cost is one acquire load of a clean shared line, since the store
happens only while the word is still clear. Fill each route and middleware
slot before the count admits it so the ordering is sound on its own, and
state the sealing rule on the router type and the affected operations.
1fd5ed9bMount assigns a fresh identity to every middleware entry it copies into the
mounted routes, so the identity the application holds for a subrouter's
component matches nothing in the destination. Remove_Middleware and
Replace_Middleware then raise while the mounted copies keep running the old
component, even though both already walk every route chain accumulating
matches, which only makes sense when one identity can appear in several
chains. Begin_Update also accepted a mounted generation, and two candidate
setters skipped the mounted guard the other mutators apply.
Solution: Carry the source registration identity through a mount
Copy mounted middleware entries verbatim so one identity names every chain
that carries the registration. Refuse Begin_Update on a mounted generation,
which covers every candidate mutator in one place, and add the guard to the
admission and challenge setters that lacked it. State the mounting rule on
the identity type, on Remove_Middleware, and in the server guide.
8244e2fbCommit raises Stale_Update while the candidate is still attached, so
Begin_Update on that object then raises Program_Error. The documented retry
loop only works because it declares the update inside the loop; hoisting it,
which is the obvious way to avoid re-entering the declaration, deadlocks the
caller into an unusable object. There is also no way to give up on a
candidate that failed validation.
The concurrency audit could not fail cleanly either. Its workers park on a
barrier that a failure before Start never opens, and the join waits for all
four to finish with no bound, so a setup error hangs the audit until the
harness timeout instead of reporting. Its assertion also could not tell a
run that spanned a publication from one that never observed a change.
Solution: Release a stale candidate and bound the audit
Release the candidate when Commit rejects it as stale, since its base is gone
and it can never be published, and leave a validation failure active so the
caller can correct and commit it. Add Abandon for an explicit discard.
Rebuild the audit around a cancellable barrier, bounded joins, and a fixed
publication count chosen odd so the closing request of every worker has one
correct answer. Workers now record a pre-publication and a post-publication
observation, so the test proves it spanned a generation change. Cover the
duplicate-name and ambiguity rejections that candidate Add defers to Commit,
and the Set_Match, Rename, Set_Middleware_Stage, and admission operations
that had no coverage at all.
5e9b0b41Every introspection operation takes its own acquire load, so a caller that
reads Route_Count and then describes each index reads two generations. A
commit that removes a route between the two calls makes the last index
invalid and Describe_Route raises Constraint_Error. The same split affects
Route_Middleware_Count with Describe_Route_Middleware. A diagnostics endpoint
enumerating routes therefore fails intermittently on exactly the deployments
runtime reconfiguration is for, and nothing lets a caller pin a generation.
Solution: Add a snapshot that pins one generation
Add a Snapshot that captures the published generation, with the whole
introspection surface available on it. Route the Router forms and the
Snapshot forms through shared configuration-level operations so both answer
identically. Retained generations already outlive any snapshot, so the
snapshot only holds a reference; its finalization is the hook a later
reclamation scheme needs.
b345dc6fCommit links every superseded configuration into the router's retention
chain, and only router finalization frees it. Retention is what keeps an
in-flight dispatch from reading freed storage, but nothing bounds it and
nothing reports it: a control plane that commits periodically grows the
chain for the whole process lifetime with no way to observe the growth and
no way to release it short of destroying the router.
Solution: Report retained generations and release them on demand
Add Retained_Generations so the growth is measurable, and Reclaim to release
every superseded generation while keeping the published one. Reclaim states
its precondition plainly: the caller must know that no dispatch is running
and no snapshot of a superseded generation is live, and that is not checked.
This keeps the request path free of reader accounting while giving a drained
server a way to return the memory.
e0a9e1b8Registration compiles each pattern into a Segment_List so dispatch does not
split it per request, but the ambiguity checks throw that away. Patterns
Overlap and Specificity both take a String and split it again, so Check_Add
re-splits every stored pattern on every registration and Commit re-splits
both sides of every route pair. Add also computed the same split twice, once
for storage and once for the score. The name-keyed Add_Route_Middleware
scanned for the name, took an identity, then re-read the router and scanned
again for that identity, which can report an identity failure for a name
that does resolve. Name comparisons allocated a String per route inspected.
Solution: Compare the segments and score each route already carries
Take a Segment_List in Patterns_Overlap and Specificity, so both the
registration check and the commit check read the stored segments and the
stored score. Order the cheap score comparison before the segment walk.
Compile each pattern once per Add. Resolve the name-keyed middleware
overload in one pass over one generation. Compare names against the stored
Unbounded_String instead of converting each one.
Cover the result with an oracle: for each of eight pattern pairs, assert the
registration decision and the commit decision are both the expected verdict.
bdcfbc8cThe guide still tells applications to bound their update rate when retained
memory matters, which describes a rate tradeoff rather than growth the
router now reports and can release. It states that direct registration must
not run concurrently with dispatch without saying the router enforces that.
It describes per-call introspection as a snapshot, which is what the new
Snapshot type actually provides, and it tells a caller to rebuild a stale
candidate without saying the candidate is already released.
Solution: Describe sealing, snapshots, and generation retention
Replace the retention paragraph with Retained_Generations and Reclaim, and
state Reclaim's unchecked precondition beside the call. Say that the first
dispatch seals the router and what a later direct registration raises. Add a
short section for traversing one generation with Take_Snapshot, and record
that Stale_Update releases its candidate while a validation failure keeps
one for correction. Every new API mention links its generated GNATdoc entry,
and all 286 generated-API links on the hand-written pages resolve.
799995fdRouter_Configuration is a large non-limited discriminated record, so RM 6.2
leaves by-copy or by-reference to the implementation for an in parameter.
Dispatch_Configuration takes a generation that way, and so does every
introspection and validation operation that reads one. GNAT passes them by
reference and the request-path measurements show no copy, but nothing in the
language requires it. An implementation that chose by-copy would duplicate a
whole generation, with every route pattern, segment list, and middleware
name, on each request.
Solution: Take every generation parameter explicitly aliased
An explicitly aliased parameter is passed by reference by definition, so
mark all fourteen Router_Configuration parameters aliased. Every actual is
already an aliased view reached through a Configuration_Access, so no call
site changes. The guarantee is now a property of the declaration rather than
of one compiler's choice.
977add4aThe published-generation word a dispatch reads and the word a commit writes
live in the same router object, so a reload can invalidate the line the
dispatchers are reading. Nothing measures that. The existing benchmark
servers run over sockets and an external load generator, where connection
and syscall cost hides an effect this small, and none of them reload the
router while traffic runs.
Solution: Add an in-process router reload benchmark
Drive routed dispatch from worker tasks over an allocation-free in-memory
transport, with one loader task republishing the configuration at a chosen
rate, and sweep that rate against otherwise identical traffic. Each worker
counts on its own stack and reports once at the end, so the harness adds no
contention of its own to the thing being measured.
The runner repeats each rate as independent process runs and reports best,
median, worst, and spread, because code layout changes per link and process
placement dominates repetition inside one run. Reporting the spread is the
point: an effect smaller than it cannot be claimed from this harness.
926fed4bEvery dispatch reads the published-generation word and the seal word. Both
sit in the same record as First_Configuration and the publication gate,
which only a commit touches, and nothing in the declaration separates the
two groups. Whatever layout the compiler happens to produce today is a
consequence of field order and padding, not a property anyone stated.
Solution: Place the two dispatch-read words in one interference region
Group them with Flyology_Cachelines.Padded_Groups, so a dispatch fetches one
region rather than two and a commit dirties the readers' region rather than
whatever else landed beside it. The two words have the compatible ownership
that a group requires: each is read by every dispatch and written only by a
commit.
This is not a measured improvement and must not be read as one. A commit
dirties the readers' region once, not once per dispatch, so the cost follows
the reload rate rather than the traffic: between 10 and 100 reloads per
second it is 0.0004% to 0.004% of throughput, four orders of magnitude below
what the reload benchmark can separate from run-to-run spread. The change is
made because the layout is then correct by declaration instead of by
accident. The benchmark now records what it can and cannot resolve, so the
question is not reopened on the assumption that a measurement is available.
8cae2c89The HTTP/2 qualification job installs its independent peer with a bare
sudo apt-get update, and no step in either workflow bounds its own runtime.
When the mirror stops answering, apt-get does not fail; it waits. The step
waits with it, and the only thing that eventually ends the run is the job
timeout, or a person noticing.
On 2026-08-19 that happened three times. Run 32226557711 on main sat on the
update for the whole 150-minute job budget before the job timeout ended it,
with nobody watching. Runs 32278682887 and 32294697524 sat there 83 and 64
minutes before someone cancelled them by hand. The same job finishes in 8 to
14 minutes when the mirror answers, and the install itself takes 11 seconds
at the median, so each of those runs spent between four and eleven times the
job's whole healthy duration waiting on one command. A run that hangs also
reads as a run that is working: there is no failure to retry, and the
concurrency group keeps the branch occupied until someone intervenes.
Solution: Bound every network step, and retry the update that stalls
Give each step that reaches the network a timeout-minutes bound, sized
against the slowest healthy observation over the preceding 60 runs rather
than against a guess, so a step that trips one has stalled rather than run
slowly. Checking out sources and configuring the index finish in seconds and
are given 5 minutes; the peer install runs 11 seconds at the median and 5
minutes at its slowest and is given 15. Installing Alire is given 15 on
different grounds: every observation of it is a cache hit, so the
observations bound the cached path and say nothing about a cold toolchain
download.
Wrap apt-get update in three attempts, each bounded on its own by timeout,
because the step bound alone would convert a stall into a failure but not
into a recovery. A wedged mirror connection now becomes a retry against a
fresh one, so a single transient failure does not fail the job, and the step
bound stays a backstop rather than the mechanism that catches the common
case. Only the update is retried: it is the command that stalled, and it is
the one here that is safe to interrupt and repeat, since a killed run leaves
partial package lists that the next run refetches. Retrying apt-get install
would risk resuming from a half-configured dpkg state to no purpose.
The same failure mode existed in two other places and is closed the same
way. The Pages workflow had no job-level timeout at all, so a stall there ran
to the six-hour GitHub default; both its jobs now carry one, and its fetch
and install steps carry step bounds. The h3spec download in the HTTP/3
conformance script used a bare curl -fL, which hangs on a stalled connection
exactly as apt-get did; it now retries with a per-attempt time limit, which
the existing checksum makes safe, because a truncated or resumed body never
survives it.
Nothing about what is installed or how anything is built or tested changes.
On a healthy run every bound is untouched and every command runs as before;
the bounds only decide how a stalled run ends.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
27a8ee59flyology_iri_benchmark times can_parse and parse_href over the pinned URL corpus. Neither reaches a component getter: can_parse constructs nothing, and parse_href consumes only the stored serialization length. The paths that read components back out of an already parsed reference, and Resolve above them, have no benchmark at all, so a change to them can be argued only from reading the code.
Solution: Add a flyology_bench harness for those paths
flyology_iri_resolve_benchmark reads all eight components at three reference lengths and resolves the RFC 3986 section 5.4 references against a short and a long base. It carries its own inputs, so unlike benchmark.sh it needs neither the Ada URL checkout nor the pinned corpus. Batches fold a value derived from each result into an accumulator handed to a barrier after the ending timestamp, and each iteration advances to a different input.
Two builds of one library cannot share a process, so before/after runs join through Flyology_Bench.Baselines, whose fingerprint refuses a comparison across operating system, architecture, compiler, or build mode while deliberately excluding the library revision. scripts/resolve-benchmark.sh selects the release mode the published medians measure and passes it to the binary for that fingerprint. Diagnose and Parse do coexist, so the validation pair is a real paired Compare.
Requested axes are Process_Resource_Metrics, the portable Darwin/Linux set; the metrics CSV reports each axis's collection status rather than leaving it assumed.
d45630c0Every public component getter reaches the stored serialization through the private Slice helper, which materialized the entire reference with To_String and then returned one range of it. The release objects show that call sited unconditionally and ahead of the zero-span test, so an absent component paid a whole-reference copy and a present one paid two allocations and two copies. Reading a four-character scheme out of a 233-byte IRI copied 233 bytes and then four. Resolve sits directly on top of that and reads Path four times and the base path twice. Remove_Dot_Segments, which runs on essentially every resolution, renormalized its input to one-based indexing through an Unbounded round trip.
Solution: Copy only the component, and read each path once
Slice now calls Ada.Strings.Unbounded.Slice, keeping the zero guard ahead of it because an absent component has First = 0 and that subprogram takes a Positive Low. The getters compile to a tail call to that subprogram on the present path and to an eight-byte bounds allocation on the absent one. The returned bounds do not change: GNAT's Slice returns the underlying Low .. High range, which is what the discarded whole-text copy was sliced by. Remove_Dot_Segments renormalizes with a constrained subtype instead of the heap. Resolve hoists the relative path and the base path into locals.
Release-mode medians on an Apple M3 Max, interleaved before/after/before/after against an unrelated build holding the load average near 25, with the repeated pre-change round as a drift control: reading all eight components falls 41.9% at 44 bytes, 33.5% at 235, and 63.8% at 2,009; resolution falls 22.2% against an 18-byte base and 15.5% against a 233-byte one. The drift control landed within 3% of its own first round. Process_RSS_Change and both page-fault axes read zero on both sides, as expected for churn the allocator reuses within a batch; wall time and thread CPU time are the axes that respond.
Component tests now name the absent components, since the zero guard is what keeps an absent span away from a Positive parameter.
4df3c149Resolve ends by parsing its assembled result into a second Reference, a heap serialization plus eight component spans, purely to establish that the bytes are well formed. A caller that stores resolved references as opaque bytes needs exactly that guarantee and none of the spans, and has no way to ask for it.
Solution: Add a serialization-returning Resolve
Diagnose establishes the same guarantee without constructing anything, and for URI and IRI it is the same computation Parse performs: both reduce to Analyze's error, and a successful non-web parse stores its input verbatim. Web mode normalizes, so there the normalized serialization is the result and the new form still parses to obtain it. The value equals Image of the Reference the other form returns, which the section 5.4 suite now asserts on every case it already covered.
The two forms share a private Assemble rather than one calling the other. Composing them the obvious way, with the Reference form parsing the serialization form's result, would make it validate twice and cost it roughly 210 ns it does not pay today.
Paired Compare in one process, release mode, against a 233-byte base: 821.3 ns/op returning a Reference against 743.4 ns/op returning a String, 9.45% less, 95% CI [1.102, 1.107], winning 100 of 100 sample pairs at an order effect of -0.46%. Isolating the validation step, Diagnose measures 211.2 ns/op against Parse's 270.1. The end-to-end saving exceeds that difference because the serialization form also skips constructing and finalizing the result Reference, not only the parse that fills it. The Reference form's median is unchanged, which is what confirms it kept its single validation.
cfb25efcResolve merges and reduces its result by appending to an Unbounded_String, which reallocates on nearly every append, and by calling Remove_Dot_Segments, which builds a second one and returns To_String of it. Segment removal is worse than that suggests: collapsing one "/../" runs a whole To_String followed by a Set_Unbounded_String. None of this is visible in the API, and all of it is on the path of every resolution.
Solution: Assemble into a String sized from the parsed references
Merging and dot-segment removal never produce more than the base, the relative reference, and the separators between them, so the serialized lengths of the two parsed references bound the result. Both are real lengths rather than Max_Length, which may be Positive'Last, and both come from parsed references rather than input bytes, because web normalization can expand a relative past what it was written with. A bound above what belongs on the stack takes one exact allocation instead, which keeps a single copy of the merge rather than a rarely exercised second implementation.
Release-mode paired medians against a 233-byte base: the Reference form falls from 823.2 to 550.4 ns and the serialization form from 743.4 to 466.5, so the change reaches the API every existing caller already uses. Resolution against an 18-byte base falls from 475.8 to 331.0. The public specification is untouched.
A procedure form writing into a caller-supplied buffer was prototyped against this and measured 4.7% below the serialization form, winning 69 of 100 pairs. That difference is the returned copy alone, which is all the wider API buys once the assembly no longer allocates, so it is not adopted.
616fdfd5Assembling into a buffer put four objects proportional to the reference length on the primary stack: the one-based copy of the dot-segment input, the merged path, the prefix taken out of the base path, and the result staged on the way out of the heap branch. The heap branch existed precisely so that a caller raising Max_Length would not put a large buffer on the stack, and then reached the stack anyway through the other three. Resolving a four megabyte base raised Storage_Error where the previous implementation returned a result. The heap branch also freed its buffer only on its two normal exits, so any exception through it leaked the allocation, and neither the bound nor the append guard was safe against wraparound in a body whose release profile suppresses overflow checks.
Solution: Keep every size-proportional object off the primary stack
Dot-segment reduction indexes its input through an offset rather than copying it one-based, so the copy is gone rather than moved. The prefix is named by bounds into the base path and written straight into the merged buffer. The merged path takes the same stack-or-heap fork as the assembly buffer, because section 5.2.4 reduces the joined path as one string and the pieces cannot be reduced separately. An extended return builds the result where the caller wants it. Both heap blocks free their allocation from an others handler as well as their normal exits.
The append guard subtracts rather than adds, so it cannot wrap, and the bound is computed in Long_Long_Integer and rejected unless it fits Natural with room to spare, which also keeps every later index lookahead inside range.
Resolving against bases of one hundred kilobytes, four megabytes and forty megabytes now all succeed, matching the previous implementation. A differential harness comparing both public forms against a verbatim reimplementation of the previous assembly reports no divergence over 429,208 cases, of which 105,202 raise on both sides.
18ecffdbResolve returning String and Resolve returning Reference differ only in what they return. Most calls select between them from context, but a call whose context supplies no type does not: comparing two resolutions with "=", or taking an attribute of one, becomes ambiguous. Both were legal while only the Reference form existed, so an existing caller of the published crate meets a compiler error whose cause is not apparent from the code in front of it.
Solution: Say so where the operation is documented
Record the overload, name the two shapes that stop compiling, and give the disambiguation. A qualified expression settles either in place, and naming the result in a constant always works. Keeping the shared name is deliberate: the failure is a compile error rather than a silent change of meaning, and the fix is local to the call.
2f3df5f8BENCHMARKS.md carries medians taken before the assembly stopped staging size-proportional locals on the stack, and its comparison between the two assemblies stitches together runs from separate sittings on a host under varying load.
Solution: Re-measure both assemblies in one interleaved sitting
Swap the two assemblies in place across four rounds, first arm repeated as a drift control, and report the resolution rows from that. The control landed within 0.4% of itself. Removing the one-based copy from dot-segment reduction moved the figures slightly in its favour, so the buffer assembly now reads 34% below the Unbounded one for both public forms rather than 33%.
4b7b1d6cCan_Parse and Diagnose report whether input parses but not how it
classifies, so a caller that admits only absolute references -- an RDF
crate deciding whether bytes are an absolute IRI, say -- has to reach for
Try_Parse. Try_Parse builds a Reference, and building one stores a copy
of a serialization that is byte for byte the input the caller already
holds. The caller reads Kind and drops it.
Solution: Report the classification without building the reference
Add Inspect, which runs the analysis Try_Parse runs and reports the
structural kind alongside the parse error. Analyze works on spans over
the caller's String, so nothing is stored and nothing is allocated. The
differential harness now holds Inspect to Try_Parse on both the error and
the kind across the whole seeded corpus.
Admitting "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" as an
absolute IRI:
allocs bytes ns
via Try_Parse 1 80 145
via Inspect 0 0 103
-100% -100% -29%
Measured with FLYOLOGY_IRI_BUILD_MODE=release. Both forms were run
alternately, eight rounds each, and the minimum taken, because the host
carries unrelated build load. Allocation counts are exact.
bc3e3217Preprocess removes tab, newline and carriage return by appending the
surviving bytes to an Unbounded_String one at a time. Almost no URL holds
any of them, so the ordinary case takes an accumulator, grows it, and
returns a verbatim copy of its own input.
Solution: Return the trimmed slice when there is nothing to remove
Scan first and build only once a byte has to be dropped, which also keeps
the controlled accumulator out of the common path entirely. The slice is
slid to one because Input_Offset and the parser below read the result at
one-based offsets.
Absolute web URLs that take the WHATWG pipeline rather than the fast
path:
allocs bytes ns
web_slow, before 25 1008 2072
web_slow, after 23 912 1964
-8% -10% -5%
web_idna, before 16 752 1661
web_idna, after 14 656 1547
-13% -13% -7%
Measured with FLYOLOGY_IRI_BUILD_MODE=release. Revisions were built into
separate worktrees and run alternately, eight rounds each, minimum taken,
because the host carries unrelated build load. Allocation counts are
exact.
WPT URL conformance stays at 919/919 with no mismatches.
bf79ef49Encode, Encode_Opaque_Path and Percent_Decode each accumulate into an
Unbounded_String one character at a time and then copy the whole thing
out with To_String. That is a heap block, the reallocations that growing
it byte by byte implies, and one full copy, for a result whose length is
a pure function of the input. Two of the callers encode a single
character at a time inside a loop, so each iteration paid all of it.
Solution: Count the result, then write it into the returned object
Each function now measures its output in a first pass and fills an
extended return object in a second. Since the count is exact, the
returned String is the only storage involved -- no accumulator and no
working buffer, so nothing is allocated and nothing is copied.
Sizing a buffer up front was the alternative, but the bound that is known
a priori is three bytes per input byte, and with Max_Length at one
mebibyte that reserves three mebibytes on the stack in a crate that does
not build with -fstack-check. Counting first costs a second pass over the
input and reserves exactly what the result needs.
Absolute web URLs that take the WHATWG pipeline rather than the fast
path:
allocs bytes ns
web_slow, before 23 912 1964
web_slow, after 17 720 1670
-26% -21% -15%
web_idna, before 14 656 1547
web_idna, after 12 592 1449
-14% -10% -6%
Measured with FLYOLOGY_IRI_BUILD_MODE=release. Revisions were built into
separate worktrees and run alternately, eight rounds each, minimum taken,
because the host carries unrelated build load. Allocation counts are
exact and do not depend on any of that.
The corpus benchmark does not move: over the pinned 100,025 URLs,
can_parse holds at 22 ns minimum and parse_href at 105 against 103, with
acceptance identical at 99999. That corpus is dominated by URLs taking
the allocation-free fast path, which never reach either function.
WPT URL conformance stays at 919/919 with no mismatches, which is the
corpus that exercises these two functions hardest.
82106435Since "The router's dispatch-read words share a region with writer state",
routing.ads groups its two dispatch-read words with
Flyology_Cachelines.Padded_Groups. That spec is one of the files the
documentation build documents, and GNATdoc walks every project in the
closure that Documentation.Excluded_Project_Files does not name, so the
private with dragged the cachelines crate in with it. Its two tagged
types, Fitted_Groups.Grouped_Array and Padded_Groups.Grouped_Array, then
appeared in the index's tagged-type section, each linked to a page of its
own -- and GNATdoc writes a page per tagged type only under
--html-oop-style, which this build does not pass. This crate's own tagged
types are linked as unit page plus fragment and resolve; those two never
could.
The site check reports both as missing targets, so the Pages workflow has
failed on every push to main since that commit landed and the published
site is frozen at the last green deploy. CI is unaffected: the two
workflows fail independently, and only one of them was ever red.
Solution: Document this crate and nothing else
Name every dependency in Excluded_Project_Files, beside the Flyology
runtime that was already there. Excluding cachelines alone would clear the
two broken links, but the closure walk is why they were reachable at all,
and the same walk had already put twenty-four pages of QUIC and allocator
internals into a reference whose own footer reads "Public HTTP API only".
The index now lists this crate's sixty-two entries, the search index
carries 2441 names with none from a dependency, and no page outside
Flyology.HTTP is written.
Removing those pages breaks no link. Their only inbound references were
the index entries that go away with them: scanning every website page and
every generated page for their file names finds nothing else pointing at
them.
Each exclusion names its project through the prefix Alire exports for that
crate rather than a path under FLYOLOGY_ROOT. The Flyology crate vendors
copies of the cachelines and allocator sources, so the two resolve to
different build directories, and GNATdoc rejects an excluded file that is
not the project it loaded -- "unable to resolve project file path", a hard
error rather than a silent no-op, so naming the vendored copy fails the
run outright. A bare project name fails the same way.
Verified with docs/api deleted first, since the workflow builds from a
fresh checkout and a stale page left behind by an earlier run would hide a
link the workflow cannot resolve. scripts/build-site.sh, which is what the
workflow runs, reports "Site check passed: 70 HTML files and 136 total
files".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
83db8c4dThe dual-stack overload of Routing.Serve divides HTTP_3_Capacity between the
address families, so HTTP_3_Capacity => 8 gave each listener four connection
slots rather than eight. Both client profiles in this test address
"localhost", which resolves to every loopback family on some hosts, and the
client's connection race completes a full QUIC handshake on each family
before it discards the losing lane. Each family therefore admits up to five
concurrent connections here: two live client profiles, two losing race lanes
awaiting release, and the raw Initial. Instrumented runs reach four of four
slots routinely.
A listener whose registry is full discards an Initial without answering it,
and no handshake-space probe timeout retransmits one. The raw handshake loop
compounded that: its receive raised Flyology.IO.Timeout_Error straight out of
the loop, so the eight attempts were never a retry budget and the first quiet
interval ended the run. The response loop had the same shape. Sizing a run of
twenty at each capacity reproduced the failure in nineteen runs at two slots
per family, seven at three, and one at four, tracking the discarded Initials
one for one.
Solution: Retransmit the handshake flight and size the listener for the load
Retain the last flight sent to the server and resend it when a receive
expires, spending the attempt budget on eight two-second probes rather than
one ten-second wait. The response loop keeps its budget for waiting, because
the server does drive application-space probe timeouts and retransmits on its
own; sixteen one-second probes stay inside the server's connection age.
Neither loop now ends an exchange on a single discarded datagram.
Raise HTTP_3_Capacity to sixteen and record the actual per-family model in
the comment, so the test stops running its listeners at full occupancy by
accident. With the retransmission in place the capacity is no longer load
bearing: at two slots per family, where the previous loop failed nineteen
runs of twenty, the raw handshake now completes in all twenty.
This is a test change. The absent handshake-space probe timeout is a real gap
in the QUIC layer and is not addressed here.
d7d87344CONNECTION_CLOSE is only reachable through the application space. An endpoint
that abandons a connection while it still holds Initial or Handshake keys has
no way to say so, and Build_Application_Close_Datagram's precondition rules it
out.
The receiving side is missing too. Initial_Space and Handshake_Space parse a
peer Transport_Close frame, count it, and discard it. A peer that did somehow
send one would be ignored, so adding a builder alone would change nothing
observable.
Solution: Report a peer close from both spaces and expose a close builder
Initial_Space and Handshake_Space now carry Peer_Closed and Close_Error in
their Process_Result and stop parsing at the frame, since RFC 9000 10.2.2 puts
the receiver into the draining state and the rest of the packet cannot change
that. Connection_Driver acts on it exactly as it already does for the
application space: Peer_Closed state, Connection_Closed status.
Build_Handshake_Close_Datagram builds the outbound half in the highest space
the connection holds, Handshake once that space exists and Initial otherwise,
so a peer that reached either level can read it. It carries the transport
APPLICATION_ERROR code that RFC 9000 10.2.3 reserves for an application close
that its encryption level cannot express, and leaves the connection Failed
because an endpoint that has begun closing must send nothing else.
Initial_Space.Build_Transport_Close_Packet passed a zero minimum packet length
for both roles, so a client-sent Initial close fell under 1,200 bytes and RFC
9000 14.1 requires a server to discard it. It now derives the minimum from the
role, as Build_Crypto_Packet already did. That also corrects the existing
client Fail_Initial_With_Close path, whose closes were unreadable for the same
reason.
c041b36dFor HTTP/3 both lanes of the address-family race run Start_Connection, which
completes a full QUIC handshake before Race.Choose picks a winner. The loser
raises Connection_Race_Lost, Close_Candidate runs, and Dispose_Connection sends
an application CONNECTION_CLOSE only when the transport reached the connected
state. A lane cancelled mid-handshake never did, so the client closes its UDP
socket and says nothing.
The server is left waiting for a handshake that will never arrive. On a bounded
listener that costs a connection slot for the whole handshake timeout, and a
peer that repeatedly starts and abandons handshakes can hold every slot.
Solution: Send a handshake-space close from the abandoned lane
Dispose_Connection now picks the close by transport state rather than skipping
it: an application close when connected, and Build_Handshake_Close_Datagram
when the connection is still in Client_Initial or Client_Handshake. Both go out
on the connection's own UDP socket before it closes, and both remain best
effort, since a disposal path cannot report a send failure to anyone.
fe4a9d4dServe_Connection waits for QUIC.Is_Connected against one deadline, the absolute
handshake timeout measured from the connection epoch. A peer that sends an
Initial and then stops answering is indistinguishable from one that is merely
slow, so its worker waits out the full budget and its Connection_Registry slot
stays taken. A cooperative peer now sends a close, but a crashed, partitioned,
or hostile one still does not.
Solution: Deadline an unconnected peer on no progress as well as elapsed time
Connection_State records the arrival of each inbound datagram, and the
pre-handshake wait takes the earlier of the absolute handshake deadline and
that arrival plus Handshake_Idle_Timeout. The absolute budget still bounds a
peer that keeps sending; the no-progress budget reclaims the slot from one that
stops.
Default_Handshake_Idle_Timeout is three seconds. Handshake retransmission on a
lossy path arrives well inside that window and counts as progress, so only a
silent peer reaches it. Serve_Listener and both Serve overloads take an
override. Routing.Serve is left alone: it gets the shorter reclamation from the
default without adding a parameter to eight overloads.
439a9fb6Both reclamation paths, the peer's transport CONNECTION_CLOSE and the server's
no-progress deadline, are only observable as the absence of a stall. No test
saturates a bounded listener with half-open handshakes, so either could regress
into the ten-second wait it replaced without a failure anywhere.
Solution: Saturate a listener of capacity N and require a fresh Initial
http3_handshake_abandon runs two phases against a capacity-three listener whose
handshake timeout is twenty seconds, far beyond every deadline it asserts, so a
slot reclaimed inside one of them cannot be explained by that timeout. Each
phase fills every slot with a handshake that will not complete, asserts that a
further probe is refused, releases the lanes, and then requires a fresh Initial
to be admitted.
The first phase abandons with closes and leaves the no-progress deadline out of
range; the second goes silent and relies on the deadline alone. Odd lanes
consume the ServerHello first so they abandon from the Handshake space, while
even lanes abandon from the Initial space, covering both protection levels. The
probe repeats its Initial while no worker answers, the way a real client's probe
timeout would, so a slot released partway through the budget is still taken up.
Each phase was confirmed to fail on its own: removing the driver's peer-close
reporting fails the first, and dropping the no-progress term from the deadline
fails the second.
439a9fb6 → updated to dac7fa90index 377df96f2 source commits
a3866c57Flyology.QUIC.Connections exposes loss recovery only for the application
packet-number space. The Initial and Handshake spaces track nothing they
have sent, discard the peer's ACK frames after parsing them, and never
acknowledge an ack-eliciting Initial or Handshake packet, which RFC 9000
13.2.1 requires immediately. Nothing arms the RFC 9002 6.2 probe timeout
those spaces need.
Both HTTP/3 drivers inherit the gap. Start_Connection loops on Receive_One
until the transport connects and Serve_Connection waits up to
Handshake_Timeout for progress; neither retransmits its own flight. A single
dropped client Initial or dropped server flight therefore costs the caller's
whole deadline rather than one probe timeout, which is how an ordinary
transient loss became a hard CI failure in the raw handshake lane of
tests/http3_server_integration.adb. That lane now works around the gap by
resending its own flight; the gap itself is still open.
Solution: Give the Initial and Handshake spaces RFC 9002 loss recovery
Each space now owns a sent-packet ledger and retains the CRYPTO ranges it
has sent in Crypto_Flight, so a probe can rebuild a range under a fresh
packet number rather than resending a packet QUIC forbids repeating. Peer
ACK frames drive Sent_Packet_Policy.Apply_ACK and yield the delivery, loss,
and RTT events the spaces previously threw away. Every packet a space builds
carries the acknowledgment it owes, and a datagram that produces no response
sends an ACK-only packet, so a peer's probe timeout clears within one round
trip.
Connection_Driver holds one path-wide Recovery_Policy state for both spaces:
a single probe count backing off together, and a probe timeout that excludes
max_ack_delay because handshake packets are acknowledged immediately.
Has_Recovery_Timeout, Recovery_Deadline, and Process_Timeout now report the
earlier of the handshake and application deadlines, and an expired handshake
timeout retransmits every unacknowledged CRYPTO range. A client whose peer
has not completed address validation sends the 6.2.2.1 anti-deadlock probe
instead. Progress that can only follow delivery of a flight retires it
without waiting for an ACK.
The HTTP/3 drivers needed almost nothing. Both already bounded each receive
by Recovery_Deadline and processed an expired timer, so widening the timer to
cover the handshake spaces made their loops retransmit; Start_Client gained a
defaulted Now so the timer has an anchor.
The raw QUIC lane of the server integration test carried a stand-in for this
gap: it retained the last flight it sent and resent it when a receive expired,
because nothing below the test retransmitted. The transport owns that now, so
the lane drives Process_Timeout against its own clock and the retained flight
is gone. Its listener sizing and the patience of its response loop are
unchanged, since neither depended on the missing timeout.
Handshake-space congestion is accounted but never gates a send: the flight
is far below the initial window and RFC 9002 permits probing regardless.
Immediate acknowledgment costs one extra packet per silent datagram,
including a padded client Initial ACK when only the ServerHello arrives;
that is what lets a peer stop retransmitting after one round trip.
Two new tests cover the loss. A deterministic transport test drives an
explicit clock through a dropped client Initial, a dropped server Handshake
flight, and the anti-deadlock probe, checking the exact first deadline, the
doubled backoff, and that an already-acknowledged Initial flight is not
retransmitted. An end-to-end test puts the routed server and the standard
client either side of a UDP relay that swallows one flight; with the probe
timer disabled it stalls for the full deadline and fails.
dac7fa90The sibling checkout pinned for local development also stood in for the QUIC
dependency's version, leaving it declared as "*". Local builds resolve through
the pin and never notice, but the pin cannot cross into the index, so anything
depending on this crate is free to resolve any QUIC release at all.
Solution: Declare the version the pin stands in for
Constrain the dependency to the development line the pinned checkout provides,
so the same version is selected once the pin is gone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.1.0-devMemory-node topology reporting for Ada
9a24bf68 → updated to 60de2c69index 8a8e9de51 source commit
60de2c69flyology.gpr imports flyology_allocators.gpr, and the manifest both declares
flyology_allocators as a dependency and pins it to the in-repo directory.
Alire honours pins only in a root crate's manifest, so a consumer resolving
flyology from the index never receives that dependency. Its solution omits
flyology_allocators entirely and gprbuild then fails with "imported project
file flyology_allocators.gpr not found". A crate whose only dependency is
flyology = "~0.1.1-dev" reproduces it in two lines.
Nothing in this repository reports the fault, because the pin satisfies the
dependency for every build performed here. The pin also hid two further
assumptions that only the in-repo directory could satisfy.
Solution: Resolve the allocators dependency from the index
Drop the pin so the declared dependency resolves the same way for this crate
and for its consumers. The published flyology_allocators 0.1.0-dev is built
from this repository at the same commit and the same subdirectory, so the
source reached is identical; only the path by which it is reached changes.
Configure the organization index in this repository's own workflows. They
carried the community index alone, because the pin meant no job here ever
resolved flyology_allocators from an index, and it is published only in the
organization index. Without that step alr reports an incomplete solution
naming flyology_allocators as missing.
Name the excluded documentation project through the imported project rather
than through a repository path. Documentation.Excluded_Project_Files listed
flyology_allocators/flyology_allocators.gpr, which resolved only while the
crate sat in this working tree; as a dependency its project lives in the
Alire build tree under a content hash. Flyology_Allocators'Project_Dir asks
the imported project where it is, so the exclusion holds wherever the
dependency is deployed and the allocator packages stay out of this crate's
generated API, where they would otherwise be documented twice.
Record the rule that produced the fault in the agent guide: no committed
pins in a crate manifest, alr pin in the consuming workspace for unpublished
work, and a declared dependency must resolve from the index. Name the
gprbuild error there too, since that is where the fault appears and it names
a project file rather than a crate.
60de2c69 → updated to 9782c81bindex 75a836be16 source commits
aeb10612The hero project map, the standalone catalogue, the library ledger, and
llms.txt each name every component. Flyology RDF publishes a website, a
guide, and three generated API references at rdf.flyology.org, but the name
appears nowhere in this repository. A reader who arrives at the site and
looks for what Flyology offers does not learn that the project exists.
Solution: Add the project to the hero map, catalogue, ledger, and llms.txt
flyology_rdf depends on flyology_iri only. It needs no runtime preparation
and starts no tasks, so it belongs with the standalone libraries. Every list
keeps its alphabetical order, so the entry sits between Flyology NUMA and
Flyology SIMD and the ledger indices shift from S07..S08 to S08..S09.
The ledger and llms.txt entries carry all three generated references, as the
Flyology Postgres entries already do, because the project site treats the
Notation3 and SPARQL APIs as first-class navigation.
The llms.txt project-structure summary also omitted Flyology Allocators and
Flyology NUMA, which the same file already listed further down. Both names
join the summary here.
Verified with the website-kit site checker, whose output is unchanged from
the pre-change baseline, and by rendering the pages: the hero, catalogue,
and ledger all name the project. The six linked URLs return 200.
2188ee0dThe figure caption claimed "One project, two adoption paths" above two cards
that already carry the same fact in their labels, so the caption restated
what the diagram shows. The two card subtitles were not parallel either:
"Core component" named a role while "No runtime dependency" named a
dependency, so neither pair member helped a reader compare the paths.
The component names below them were plain list items. A reader who found the
name that matched the problem had no way to reach the component from the
diagram and had to scroll to the catalogue to find the same name again.
Solution: Drop the caption, make the subtitles parallel, and link the names
The subtitles now read "Prepare the runtime" and "Add a crate", which state
the two adoption paths as the actions they are. The figure keeps an
accessible name through aria-label, and the figcaption rule leaves the
stylesheet with the element it styled.
Each component name becomes a link to its best available destination, which
is the destination the catalogue below already uses: the project site where
one exists, then the guide, then the generated API, then the source tree.
The pill moves from the list item to the anchor so the whole badge is the
hit target. Links open in a new tab and carry no underline, as requested, so
hover brightens the border and adds a faint fill instead. Each list states
the new-tab behavior once in its aria-label rather than repeating it in
twelve hidden spans.
Verified by rendering the page: every badge computes text-decoration-line
none and cursor pointer, and the hovered badge is distinguishable from its
neighbors. The site checker reports one new line against the baseline,
api/flyology_debug/ from the new Debug badge, which resolves once
scripts/docs.sh generates the GNATdoc output that this partial build omits.
5dfd5d36Two measurement runs that share a machine produce two wrong answers, and
neither run can tell that it happened. The problem is not confined to
benchmarks: a load generator, a soak test, or a profiler makes the machine
loud, and a benchmark needs it quiet. Both want the same thing from opposite
directions, and nothing lets them find each other.
Worse, two runs that each suspend collection when they detect interference
are symmetric controllers observing each other. Each pauses because the other
is loading the machine, both then observe quiet, both resume together, and
both immediately pause again.
Solution: Specify a host CPU lock convention and implement it
The convention is named for the resource rather than for benchmarking, so
that the tools which create load take the same claim as the tools which need
its absence. Conflict detection is flock and nothing else: liveness is the
lock itself, so a holder that dies releases with no cleanup path, no stale
registry entries, and no pid-reuse hazard.
A machine-wide claim takes LOCK_EX on the claim file; a claim on individual
CPUs takes LOCK_SH there and LOCK_EX on one file per CPU. Disjoint core-scoped
claims therefore proceed concurrently while a machine-wide claim excludes them
all, with no registry and no arbiter. Per-CPU claims are acquired in ascending
order, because a total order is what stops two callers with overlapping sets
from deadlocking on each other's partial claims.
The path is a single deterministic default rather than a writability-probing
fallback chain. A chain reintroduces the failure it was meant to prevent:
/run/lock is 1777 on Debian and root-owned 0755 on Red Hat derivatives, so a
root process and an unprivileged process on one machine would probe
differently, land on different files, and each conclude it holds the machine.
$TMPDIR is avoided for the same reason, being per-user on macOS.
File content is diagnostic and is never consulted to decide conflicts. It is
still specified and parseable, one key=value pair per line, because a reader
must be able to name the holder; cwd is included because tool and pid alone do
not distinguish one checkout or CI workspace from another. Readers open the
file without locking, since a shared lock would block behind the very holder
they are trying to identify, and must tolerate content that is empty, stale,
or half-written.
A claim is not proof of exclusivity, and the implementation does not present
it as one. systemd PrivateTmp= bind-mounts a per-service directory over /tmp,
so two services share every CPU and cannot see each other's claim files; that
case is detected from /proc/self/mountinfo and reported as namespace-scoped.
Separate containers are not detectable from inside and need an explicit shared
path. A path can be proven private but never proven machine-wide.
Only the C macros live in the native unit. GNAT's C_Variadic_2 convention
calls open() correctly even on Darwin/AArch64, where variadic arguments are
passed differently from fixed ones, so the protocol, the file grammar, and the
/proc reader are all Ada. The convention compiles on every GNAT this crate
supports, checked against 13.2.2 through 16.1.0 against an alire.toml floor of
13, so no compatibility shim is carried.
Verified by building the crate, and by taking claims on disk: the file matches
the published grammar, parses with awk -F=, is created 0666, and per-CPU files
are named beside the machine file.
13b71a5bCPU_Quiescence samples host counters once, before clock characterization, and
then never looks again. A machine that goes busy halfway through collection
produces a result that reads as clean, and nothing in the measurement, the
console card, or the machine-readable output says otherwise. The gate's own
documentation admits it "cannot prevent new load from arriving after
collection begins", and until now that was the end of the story.
The obvious repair is not available either. Host utilization cannot simply be
compared against a limit during collection the way the preflight does it,
because by then the benchmark thread saturates a core and every sample would
trip the limit.
Solution: Watch for foreign load during collection and say what was found
Foreign load is host busy time minus this process's own CPU time, so load the
benchmark itself creates is correctly not foreign. The estimate scales the
host busy ratio by wall time rather than converting ticks, so the platform
tick length never enters the arithmetic. Cross-checked against top on a loaded
machine, the two agree to within 0.1 percent.
Three responses form a ladder. Observe records and keeps everything. Retake
discards the contaminated window and collects it again. Pause waits for the
host to settle, re-warms, and then collects it again. The re-warm is not
politeness: a resumed run has cold caches, predictors, and frequency state, so
without it the first sample after a pause is the outlier the pause existed to
avoid.
Nothing adjusts a reported statistic. A skew factor derived from observed load
would be invented data, because the cost of interference depends on whether
the contention is an SMT sibling, a shared cache, memory bandwidth, or another
socket, and the same foreign share can mean no slowdown or a large one. Runs
are annotated or repaired, never corrected.
Observation happens between timed samples over windows of whole collection
units, where a unit is one sample, one comparison pair, or one balanced
multi-way round. A response therefore never splits the two halves of a pair
and never lands mid-round, where it would spread interference unevenly across
the cases a round exists to compare fairly. Windows carry a margin over the
configured minimum: sized to only just reach it, ordinary batch jitter drops
them below it and they silently degrade to observation. Host counters are
tick-based, so a window that still falls short is recorded but never acted on.
Discarding a window rolls back what the window changed. Per-sample values are
written by index and survive a retake intact, but the position tally of a
comparison pair and the process-telemetry summaries are running sums, so a
discarded pass gives them back. Without that, a run with eighteen retained
samples and twenty-four retakes would report elapsed time and an average CPU
share covering forty-two samples, most of which were thrown away.
Budgets degrade rather than abort. An exhausted retake or pause budget
continues under Observe and reports it, and paused time is excluded from
Maximum_Sampling_Time because waiting for the host is not collection.
Placement is optional and sharpens attribution: with a strict binding every
other CPU's busy time is foreign outright, and the placed CPU's share is its
busy time minus the placed thread's own. SMT siblings join the watched set,
because a process saturating the sibling of the placed CPU slows the
measurement while leaving the placed CPU's own share clean, which makes
sibling-blind core-scoped observation worse than host-wide observation rather
than better. Placement is Linux-only in practice; Apple Silicon implements no
thread affinity and rejects every request, so the Host_Control documentation
no longer describes Darwin placement as merely advisory.
Placement binds only the calling thread, so another thread of this process can
occupy a watched CPU, where its time is indistinguishable from foreign load.
Each window therefore reads process CPU as well as thread CPU, and their
difference bounds how much of the watched capacity those threads could account
for. Once that bound exceeds the configured foreign limit, the core-scoped
answer can no longer address the question the limit asks: the run drops to
host-wide observation, which is well defined for a multi-threaded process, and
reports Attribution_Diluted. Otherwise a scheduler benchmark run under
placement would spend its retake budget discarding samples over load it
created itself. The window that reveals the dilution is discarded rather than
recorded, because its value was computed under the attribution being
abandoned.
The direction of that fallback is deliberate. Core-scoped attribution
subtracts only the placed thread, which over-reports interference when other
threads share the watched CPUs; subtracting the whole process instead would
deduct time our threads spent on CPUs outside the watched set, under-report
foreign load, and let contaminated data pass as clean. Over-reporting shows up
in the retake and contamination counts; under-reporting shows up nowhere.
The host CPU claim is taken before the preflight gate, since waiting for a
quiet host and then blocking on the claim leaves the quiet verdict stale.
Everything observed reaches the console card, the CSV schema, and the JSON
object. That visibility is the point: a run that had to repair itself must say
so, or a heavily repaired result reads as a quieter machine than the one it
ran on.
Verified with ./scripts/test.sh on Darwin/aarch64 and in an Ubuntu 24.04
container on Linux/aarch64, and under qemu-system-aarch64 across four CPU
topologies. Core-scoped attribution asserts that it closed an observation
window, so the thread-CPU probe it depends on cannot fail silently. Two
assertions were checked against builds that defeat them: one with sibling
collection disabled fails the sibling assertion under cores=2,threads=2 and
passes without SMT, and one with the dilution check disabled fails the
fallback assertion while a task of the test's own process burns CPU. Retake
and pause are driven by a spawned process, because load raised inside this
process is its own CPU time and is subtracted by design.
845bdacawebsite/AGENTS.md governs hand-written pages under guide/**. The interference
section met none of its API-link requirements and several of its style rules.
Every public entity it introduced went unlinked: Interference, Placement,
Host_Lock, the three Interference_Response literals, Maximum_Retakes, Window,
Rewarm_Time, Maximum_Foreign_CPU_Percent, Include_Siblings, Require_Strict,
Attribution_Diluted, and Environment. The same page already links
CPU_Quiescence, Stable_Time, and Timeout, so the new section also read
inconsistently against its own neighbours.
Solution: Link the API entities and apply the three prescribed reviews
The three reviews ran in sequence and are labelled here, since separate review
agents were unavailable.
Technical. "Foreign load is host busy time minus this process's own CPU time"
held only without placement, because core-scoped attribution subtracts the
placed thread rather than the whole process; the sentence now says so, and the
placement paragraph states which CPUs the harness measures. "The re-warm is
required" overstated a configurable field, so the text names Rewarm_Time and
says what a zero value leaves behind. "When the difference exceeds the
configured limit" named neither the limit nor the compared quantity, which
also left Maximum_Foreign_CPU_Percent unlinked. Two harnesses that both pause
do not deadlock as claimed; they alternate between pausing and resuming.
Editorial. "This matters most" and "Without it" both referred back to
something unnamed. Both now name their subject.
Controlled language. "Sharpens attribution" was figurative and is now
"narrows attribution to the placed CPU and its SMT siblings". "A run that
repaired itself reports that fact" personified the run; the report is the
actor, and it states counts.
Verified with ./scripts/build-site.sh and the site checker, which passes 144
HTML files. All fourteen API links resolve to an existing anchor in the
generated GNATdoc output, and each target matches the entry the API search
index gives for that entity. No sentence in the section exceeds the 25-word
review signal.
edf788efThe interference section described three capabilities without saying that a
run acquires none of them unless it asks. BENCH 07 states that contract for
the preflight gate, and the page marks the default value of a choice where one
exists, as it does for Equal_Time and Balanced_Rounds. The new section did
neither, so a reader could not tell that enabling the watch alone changes
nothing about the collected data.
The claim file was also absent. The page named three environment variables but
not the one path that other tools must agree on, leaving the coordination
surface a click away in the convention document.
Solution: State the defaults, the claim path, and the budget interaction
Observe is now marked as the default response, in the form the page already
uses. The closing paragraph mirrors BENCH 07: the watch, placement, and the
host claim are disabled by default, so an existing run acquires no new wait or
failure condition. The opening sentence calls the watch optional where the
reader first meets it.
The claim paragraph names /tmp/host-cpu.lock, the Path field that overrides it
for one run, and the HOST_CPU_LOCK_PATH variable that overrides it for every
tool following the convention. An earlier draft named only the variable, which
implied it was the sole override; the technical review caught that against
Resolved_Path and Try_Acquire.
The Window row gains the limit that qualifies it. A window covers whole units,
so a window spanning many short samples can exceed Maximum_Retakes on its
first discard. Collection then continues under Observe and reports the
exhausted budget, which otherwise looks like the response never ran.
Numeric field defaults are deliberately not repeated here. No other section of
the page tabulates them, every field now links to its generated entry, and a
duplicated value is one that can go stale.
Verified with ./scripts/build-site.sh and the site checker, which passes 144
HTML files. All fifteen API links resolve to an existing anchor, and each
target matches the entry the API search index gives for that entity, including
the ambiguous Path simple name, which resolves to Host_Lock_Policy.Path. No
sentence in the section exceeds the 25-word review signal, and no entity is
linked twice.
4fbe5020recording_smoke drives two benchmarks from four tasks and asserts that the
slow benchmark's recorded median wall time exceeds the fast one's. The
separation comes from delay 0.000_2 against delay 0.000_8. A relative delay
is rounded up to the host timer tick, and on a Linux kernel with a coarse
tick both requests wake at the same boundary near 3 ms. A four-task probe
with no recorder involved shows the first iteration of each task honoring
the request and every later one landing on the same plateau:
0.0002 s: 323 3710 2829 2972 3069 2947 ... (us)
0.0008 s: 1167 3307 2482 3625 2793 3045 ... (us)
The two recorded medians then arrive at 2.98 ms and 2.99 ms, ordered by
scheduling noise alone. On Ubuntu 24.04 with GNAT 16.1.0 on aarch64 the
assertion fails about half the time: 9 of 20 runs on a clean checkout. macOS
resolves the two delays apart, which is why the test has held there. The
independent comparison checked two assertions below rests on the same
separation and fails with it.
Solution: Separate the distributions by work the workers perform
Each worker spins a volatile rotate-and-xor accumulator, 20_000 rounds for
the fast benchmark and twenty times that for the slow one, the idiom the
file's concurrent PMU-session block already uses. That is roughly 33 us
against 680 us, a twentyfold gap read by a nanosecond wall clock, so no
timer resolution enters the measurement. It also costs less than the
sleeping it replaces: the slow workers take about 14 ms rather than 60 ms.
The assertion stands, since the ordering is the invariant worth holding, but
its message now carries both medians so a failure on an unusual host reports
numbers rather than a verdict.
Verified on the failing environment, an Ubuntu 24.04 container with GNAT
16.1.0 on aarch64: 30 of 30 runs pass where HEAD fails 9 of 20 in the same
container, 20 of 20 under a two-CPU quota, and 10 of 10 with 24 competing
spinners on that quota. scripts/test.sh passes three times from a fresh
source export there and once on macOS. A calibration probe over 105 further
trials across those CPU configurations held the ratio between 14x and 30x
with no inversions and Relative_Change_Percent positive throughout.
ae5b52d7Flyology.Observability.Group_Snapshot carries thirty fields covering queue
depths, wait categories, and cumulative counters, but none of them reports how
much of an event loop's time was spent doing work. Pool imbalance is therefore
invisible from outside the process: a group with a deep ready queue and a group
that is idle produce the same evidence once their momentary queue depths match.
Made_Progress detects a stalled loop but not a saturated one, and its comment
already records why a per-dispatch clock read was rejected as the answer.
Solution: Time the poller wait a loop takes when nothing is ready
An event loop enters Wait_Batch only when its ready queue is empty, so the idle
path is both the exact complement of the loop's working time and a path a busy
loop never repeats. The scheduler stamps the wait before releasing the group
lock and closes it after reacquiring, keeping the in-progress wait in the group
so an observer adds it rather than charging a long block as busy time. Nothing
is added to dispatch, context switch, or any other hot path.
Group_Snapshot gains Uptime_Nanoseconds, Idle_Nanoseconds, and Idle_Waits, and
the runtime snapshot ABI moves to version 6. Idle time is clamped to uptime and
both come from one clock reading, so callers can always take the difference or
the ratio. tests/native/process_lifecycle_probe.c pins that ABI and its layout,
and moves with it.
The cost is measured rather than asserted. runtime_callback_bench adds
poller_idle_cycle for the instrumented path and monotonic_clock_read for the
unit of work it adds; benchmarks/idle_wait_rate reports the rate a loop
actually reaches, because the effect is far below what a differential timing
run resolves on this host. On macOS/AArch64 a reading costs about 20 ns, a
socket ping-pong reaches roughly 32,000 to 38,000 waits per second per loop,
and a loop doing nothing else reaches roughly 120,000 to 160,000: under 0.2%
and under 0.7% of a loop's time respectively.
Checks run: ./scripts/test.sh (pass, including the new
observability_utilization_smoke), ./scripts/docs.sh (pass, no new warnings),
./scripts/bench-runtime.sh.
6c4bfc3cStep 10 of the guide lists the readings that diagnose a group, but every one of
them describes queue contents. A reader who wants to know whether an event loop
is saturated has no entry to follow, and the utilization fields the runtime now
publishes appear only in README.md and the generated API reference.
Solution: Document loop utilization where the guide diagnoses a group
A new subsection explains what the runtime measures, why the subtraction is
always valid, and the two readings that look alike: a group whose tasks all
wait resembles a group with nothing to do, and a task that never suspends keeps
its loop out of the poller and reports as fully busy. A decision row places the
measure next to the queue-depth readings it qualifies. The cost paragraph names
the measured figures and the script that reproduces them, rather than asserting
that the accounting is cheap.
The worked example is compiled and run by tests/guide_utilization_example.adb.
The first draft of that example did not compile, because Counter is a modular
subtype and the subtraction needs a use type clause the guide had not shown. A
test main is the only check that keeps a printed example honest about the
visibility a reader must supply.
Checks run: ./scripts/test.sh (pass, including the new
guide_utilization_example), ./scripts/build-site.sh, and check-site.mjs over
the built site as CI runs it.
fb881640The website/AGENTS.md technical review is required for any changed capability,
limit, or timing claim, and running it on the new guide section found four
faults.
Two are wrong rather than unclear. The guide told a reader that `Counter` needs
a `use type` clause because the subtype is modular; the clause is needed
because the operators of a type declared in another package are not directly
visible, so the stated rule teaches the wrong thing. The guide also said an
event loop waits in its poller only when no task can run, but the loop polls
with a zero timeout while work is ready, and the same section counts that
polling as busy time.
The remaining two narrow a claim that had become broader than the code.
"Dispatching, polling with work ready, or running task code" reads as an
exhaustive account of a loop's busy time and omits timer promotion, file
submission, and cancellation processing. That wording appeared in the guide,
README.md, and the Group_Snapshot field documentation, so all three change
together. `Group_Snapshot` and `Snapshot` appeared only inside the code block
and were linked nowhere in prose, which the API-link rule does not allow.
Solution: Correct the two faults and narrow the two broad claims
The editorial and controlled-language passes contributed three further changes:
one repeated paragraph opening, "idle fraction" used before the section
introduces it, and "reading" carrying both a clock read and a snapshot.
Checks run: ./scripts/docs.sh (pass, no new warnings),
./scripts/build-site.sh with check-site.mjs over the built site,
and observability_utilization_smoke, guide_utilization_example, and
observability_smoke rebuilt and re-run.
bca8ad7fscripts/build-site.sh writes the complete site to build/site, but nothing
records how to serve it. Reading a guide page as it will be published means
inventing a static-server invocation each time, and picking a port that does
not collide with a development server already running.
Solution: Record a launch configuration that serves the built site
The entry serves build/site on port 8231. It reads the build output and does
not produce it, so run scripts/build-site.sh first and again after editing
anything under website/.
Checks run: served build/site through the entry and loaded the guide page.
104d406fThe macOS body queries the flat hw.l1dcachesize sysctl. On a heterogeneous
Apple silicon host that name reports the efficiency-core capacity, not the
performance-core capacity: one 12P/4E host reports 65536 bytes for the flat
name against 131072 for hw.perflevel0.l1dcachesize. Flyology_Cachelines.
L1_Data_Cache_Size therefore reports half the performance-core L1d capacity,
and L1_Data_Cache_Slots derives its slot count from that halved value.
The reader also assumed every queried name stores a 64-bit value. It read
each name into one 64-bit object and accepted a short reply, so a 32-bit
value was correct only because Darwin is little-endian and the object was
zero-initialized. The per-level names are 32-bit.
Solution: Prefer the highest performance level and read the reported width
macOS 12 and later describe a host whose cores are not identical as ordered
performance levels, where hw.perflevel0 is the highest-performing level. The
macOS body now queries hw.perflevel0.l1dcachesize and falls back to the flat
name only on a host that publishes no performance level, such as macOS 11 and
earlier or an Intel Mac. Querying hw.nperflevels first would add nothing: the
per-level name exists exactly when the host publishes performance levels.
Hardware_Cache_Line_Size is unchanged, because Darwin publishes no per-level
line size and both Apple silicon core types use the same line.
The public API keeps its Cache_Query_Result surface. Reporting per-level
geometry is a separate design question, and it should not ride along in a
detection correctness fix.
The sysctl reader moves into a new private Flyology_Cachelines.Macos child,
mirroring the existing Flyology_Cachelines.Linux split between mechanism and
policy. It requests each name's stored width before reading the value and
rejects any other width, so a 32-bit name, a 64-bit name, a string, and an
absent name are all handled without depending on byte order. Failed and
malformed queries still degrade to Unavailable rather than raising.
The GNATdoc comments on L1_Data_Cache_Size and L1_Data_Cache_Slots, the crate
README, and the website cachelines guide now state that a host whose cores are
not identical reports one core type, and which one each platform reports. A new
macOS detection test mirrors the Linux one: it exercises the sysctl reader
directly and checks that the reported capacity is the highest performance level
the host publishes.
Checks run: ./flyology_cachelines/scripts/test.sh (the native test now reports
131072 bytes of L1 data cache on a 12P/4E host, and the new macOS detection
test passes) and ./flyology_cachelines/scripts/docs.sh (zero GNATdoc warnings).
d3af8647Flyology_Cachelines.Linux_Testing asserts that both Detect_From_Sysconf and
Detect_From_Sysfs report a value. Only their composition is guaranteed:
glibc answers _SC_LEVEL1_DCACHE_SIZE and _SC_LEVEL1_DCACHE_LINESIZE from
CPUID on x86-64, but reports zero on aarch64, where sysfs is the only
mechanism. Detect already treats sysconf as optional and falls back to
sysfs, so the test is stricter than the code it covers.
The test therefore fails on aarch64 Linux with "sysconf cache detection
failed". CI runs ubuntu-latest on x86-64, where sysconf answers, so the
failure is invisible there. It reproduces on aarch64 Linux, which AGENTS.md
tells contributors to prefer on Apple silicon because it is native and
faster.
Solution: Require the composition and check each available result
Run now requires that at least one mechanism reports a value, and checks
consistency only for a mechanism that reported one. That is what the
production detector promises: it composes sysconf and sysfs, and returns
Unavailable only when neither answers.
Checks run: the crate suite on aarch64 Linux in a container, where the
detection test now passes, and on macOS, where it is not built.
04658ed7Flyology_Cachelines.Linux reads one L1 data-cache geometry: sysconf answers
for the calling CPU, and the sysfs fallback reads cpu0. A host whose cores
are not identical has more than one geometry, and neither mechanism says
which core the reported one belongs to, or that others exist.
macOS already publishes ordered performance levels, so the macOS body can
prefer the highest-performing one. Linux publishes per-CPU descriptions and
no single ordering, so the equivalent information has to be derived before
any public per-core-type query can work on both platforms.
Solution: Group CPUs into core classes and record what ordered them
Detect_Core_Classes enumerates the CPUs the host lists as present, reads
each one's L1 data-cache geometry and capacity, groups CPUs reporting
identical values into a class, and counts each class.
Classes are grouped on capacity and geometry together. Grouping on geometry
alone merges two core types that happen to share an L1 data-cache size,
which real designs do.
Ordering uses whichever key separates the classes, and Core_Classes reports
which one did, so a caller can tell an authoritative order from an inferred
one:
- Host_Reported: the kernel's per-CPU capacity values separated them.
Capacity is the scheduler's own measure of relative CPU performance and
accounts for microarchitecture and clock. Architectures selecting the
generic topology code publish it under cpuN/cpu_capacity.
- Inferred: no capacity separated them, so they are ordered by descending
L1 data-cache capacity. That holds on current hybrid parts and carries
no meaning on a host whose classes differ some other way. x86-64 does
not publish capacity, so its hybrid parts land here.
- Unordered: fewer than two classes, or nothing separated them.
Cores and CPUs are counted separately. SMT siblings share one L1 data cache,
so the cache's sharing list identifies which CPU represents a physical core.
Counting logical CPUs as cores overstates a 24-core host with two threads
per core by a factor of two.
Detection fails closed. A missing or malformed CPU list leaves only CPU 0 to
enumerate, enumeration is bounded, a CPU whose description cannot be read is
skipped, and a host reporting more classes than the table holds reports none
rather than a table that silently omits a class.
Detect_Core_Classes takes the CPU root as a parameter defaulted to the real
sysfs path. Grouping, ordering, and the SMT distinction cannot be reached on
a uniform test host, so the detection test drives them with committed
fixture trees: two classes separated only by capacity, two classes separated
only by geometry with the smaller listed first, SMT pairs sharing one cache,
and a CPU list with a gap.
Nothing calls Detect_Core_Classes outside the test yet. The public
per-core-type query, and the elaboration cost of walking every CPU, belong
to the change that adds that query.
Checks run: the crate suite on aarch64 Linux in a container and on macOS.
Detection was also replayed against captured CPU descriptions from a 48-CPU
x86-64 host with two threads per core, which reports 24 cores against 48
CPUs and no capacity, and from an 80-CPU aarch64 host, which reports 80
cores against 80 CPUs and a published capacity.
ba8438daFlyology_Cachelines reports one L1 data-cache capacity. A host that combines
core types has one per type, so a single value silently describes whichever
type the platform body happened to pick and gives a caller no way to ask
about the others, or to learn that others exist.
The two platforms also disagreed. macOS reported the highest performance
level, while Linux reported whichever cache its own mechanism described:
sysconf for the calling CPU, sysfs for cpu0. The same call therefore meant
different things on the two platforms.
Solution: Make the core class an explicit parameter of every cache query
Core_Class names one class of cores that report a single cache geometry.
Core_Class_Count reports how many the host distinguishes, Core_Class_Cores
and Core_Class_CPUs describe each one, and L1_Data_Cache_Size and
L1_Data_Cache_Slots take the class they describe. The parameter defaults to
Fastest_Core_Class, so existing calls keep compiling and now mean the same
thing on both platforms: the class the host ranks highest.
Core_Class_Ordering reports what produced that rank, because it is a host
fact on some platforms and the crate's inference on others. macOS performance
levels and the Linux per-CPU capacity values are Host_Reported. A host
publishing no rank is Inferred, ordered by descending L1 data-cache capacity.
Fewer than two classes is Unordered.
Cores and CPUs are reported separately. Sibling CPUs under simultaneous
multithreading share one core's L1 data cache, so a 24-core host with two
threads per core reports 24 cores and 48 CPUs in one class.
Hardware_Cache_Line_Size takes no class. macOS publishes no per-class line
size, and the core types of current heterogeneous parts share one line size,
so a per-class query would have one answer to give.
The host is now inspected on first use rather than during elaboration.
Describing every class means reading one description per CPU on Linux, which
measures 14 ms for 48 CPUs and 24 ms for 80. A program that links this crate
and never asks about the host should not pay that at startup. The inspection
is idempotent, so a race on the first query can duplicate work but cannot
produce a value neither caller computed; the ready flag is atomic and is set
after the result is stored.
Flyology_Cachelines.Platform now answers one Detect call describing the whole
host, and the root package body owns the single memoization. The two Linux
bodies share one translation and differ only in what they report when sysfs
describes nothing: the x86-64 body can still read the calling CPU through
CPUID. Flyology_Cachelines.Linux drops its own class and ordering types in
favour of the public ones, so the crate has one Core_Class.
The website cachelines guide is corrected alongside the API. Adding the Class
parameter changed the generated GNATdoc anchors for L1_Data_Cache_Size and
L1_Data_Cache_Slots, so the guide's links to them became dangling fragments;
both now point at the regenerated anchors. The guide also claimed detection
happens during package elaboration, which is no longer true. The new entities
are linked to their generated entries on first mention.
Checks run: the crate suite on macOS, where a 12P/4E host reports two
host-ordered classes of 131072 and 65536 bytes across 12 and 4 cores; on
aarch64 Linux in a container; and on an x86-64 Linux host with 24 cores and
two threads per core, which exercises the CPUID body and reports 49152 bytes
across 24 cores and 48 CPUs. GNATdoc builds with no warnings.
9782c81bFlyology_Cachelines reports L1 but not L2, so a caller sizing a working set
against the level it will actually spill into has nothing to read.
The flat sysctl this crate would naturally have used is wrong in the same way
hw.l1dcachesize was, and by a larger factor. On a 12P/4E Apple silicon host
hw.l2cachesize reports 4194304, the efficiency cluster's cache, against the
performance cluster's 16777216. Adding L2 before core classes existed would
have reproduced the original defect at four times the size instead of two.
Reporting a capacity alone would also mislead. L2 is rarely private: that same
host shares 16 MiB across six performance cores and 4 MiB across four
efficiency cores, so neither figure is a per-core budget.
Solution: Report L2 capacity and how many cores share it, per class
L2_Cache_Size and L2_Sharing_Cores take the same core class as the L1
queries. A sharing count of one means the cache is private to a core; a
larger count means the reported capacity is divided among that many cores.
The GNATdoc on each function points at the other, because reading either
alone invites the per-core mistake.
macOS reads hw.perflevelN.l2cachesize and hw.perflevelN.cpusperl2. Linux
reads the level 2 entry of each CPU's cache description, counting the CPUs
its shared_cpu_list names. Both counts arrive as logical CPUs and become core
counts through the class's own threads-per-core ratio, so a host with two
threads per core does not report twice the cores sharing a cache.
Parsing a CPU list needs more than its leading index, so the Linux body gains
List_Count, which sums the single indexes and inclusive ranges a list names.
Describing L2 costs no extra file reads: the level 2 entry is found in the
same pass over the cache indexes that already locates the level 1 data entry.
The website cachelines guide gains the same pairing, with both names linked
to their generated entries.
Checks run: the crate suite on all three supported targets. A 12P/4E macOS
host reports 16777216 bytes across 6 cores for its performance class and
4194304 across 4 for its efficiency class, and the macOS test asserts that
these come from the performance level rather than the flat name. An aarch64
Linux host reports 16777216 across 6 cores. An x86-64 Linux host with 24
cores and two threads per core reports 2097152 across 1 core, where sysfs
gives 2048K shared by CPUs "0,24"; that run also compiles the CPUID body,
which builds nowhere else. Fixtures cover a per-class shared L2, an L2 shared
across SMT siblings, and a private L2. GNATdoc builds with no warnings.
9782c81b → updated to 7be049fcindex 6e2db0ef1 source commit
7be049fcThe two OpenSSL install steps in the test job carried no bound, so a mirror
that stops answering leaves apt waiting rather than returning an error. On
2026-08-19 run 32297071503, job 96211136305 sat on "Install OpenSSL 3 on
Linux" for 32 minutes and had to be cancelled by hand; it was the only check
blocking PR #49. Its log shows apt-get update fetching noble-security
InRelease at 20:13:04 and producing no further output until cancellation. The
macOS half of the same job passed, so nothing was wrong with the tests.
Left alone the job would have run to GitHub's six-hour default. Nothing else
in either workflow was bounded either: the checkout, the Alire install, the
index configuration, the Java and TLA+ downloads, and the GNATdoc install all
reach the network under the same default, and no job in this repository
declared a timeout at all, unlike the sibling repositories.
Solution: Bound every job and every step that reaches the network
Each job now declares a timeout-minutes, and so does each step that fetches
something. The bounds are generous multiples of what healthy runs measure
over the last 60 successful runs of each workflow, so they can only fire on a
stall. Jobs: test observes at most 17 minutes on macOS and 11 on Linux and is
bounded at 60; proof observes 6 minutes and TLA+ 7, both bounded at 30; the
pages build observes 69 seconds and is bounded at 20, its deploy 10 seconds
and bounded at 15. Steps: everything network-touching is bounded at 10
minutes against observed maxima of 1 to 6 seconds, except the Alire install,
which downloads the toolchain, observes 100 seconds, and is bounded at 15.
A step timeout alone would only have converted the 32-minute hang into a
10-minute one, because the stall is inside a single apt-get update that never
returns. That command is now capped per attempt with timeout(1) and retried
twice with 15 and 30 second backoff, so one unresponsive mirror costs a
retry rather than the job. The install that follows is capped as well but not
retried: a package that cannot be resolved should fail on the first try. The
worst case is three 120-second attempts plus a 10-second kill grace, 45
seconds of backoff, and a 190-second install, which is 10m25s and lands
inside the 12-minute step bound, so the annotation naming the transient
mirror is what the log ends on rather than a bare cancellation.
GNU timeout is used directly rather than scripts/run-with-timeout.sh. That
helper exists for portability to macOS, and this step is Linux-only.
The TLA+ download gains --connect-timeout 30 and --max-time 120 alongside its
existing --retry 3, because curl retries a failed transfer but not a stalled
one, and --max-time resets per attempt. Four bounded attempts stay inside the
step's 10 minutes.
Build, test, proof, and model-checking steps keep no step bound. Their
duration is workload-dependent, a bound sized for them would be a performance
budget rather than a stall detector, and the job timeout already covers them.
Nothing about what is installed, or how anything is built or tested, changes.
Checks run: both workflows parse. The retry script was extracted from the
committed YAML and driven under bash -e against stub apt-get invocations for
five outcomes: first-try success runs one update and one install; two
failures then success runs three updates and installs; three failures exit 1
with the annotation and never install; an install failure propagates its exit
status without retrying; and exit 124, which is what timeout(1) reports when
it kills a stalled command, is treated as retryable. The real commands were
then run in an ubuntu:24.04 container on coreutils 9.4, the runner's version:
the bounded update succeeds, interrupting one mid-flight reports 124, a
retry after that kill still succeeds rather than inheriting a poisoned apt
state, and the bounded install still yields OpenSSL 3.0.13 through pkg-config.
The convention is recorded in AGENTS.md so it holds for new steps, and it
matches the job-level timeouts already used in flyology-http and
gnat-patches, whose install steps want the same treatment.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7be049fc → updated to a53da35dindex b55699621 source commit
a53da35dThe allocator crate is developed alongside the runtime in this repository and
released on the same development line, but the dependency on it is declared as
"*". Nothing stops a dependent from resolving an allocator release built for a
different line, and the index has been carrying a tighter constraint by hand to
compensate.
Solution: Constrain allocators to its development line
Declare the line the allocator crate actually publishes, so the constraint
travels with the manifest instead of being reapplied wherever it is indexed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.1.1-devPostgres client and protocol server for Flyology tasks
92212697 → updated to a8af8e1findex 510cfcf61 source commit
a8af8e1fThe native PostgreSQL parser and generated owned AST support five grammar versions, but CI does not exercise the upstream regression corpus. Parser generator, scanner, semantic-action, and conversion defects can therefore survive the existing focused cases.
Solution: compare every PostgreSQL regression file against pinned oracles
Acquire and checksum the exact upstream corpora, recover oracle-valid statements from psql-oriented and expected-error files, compare native and owned AST layers, cache the inputs in CI, and correct the generator/runtime defects exposed across PostgreSQL 14 through 18.
a8af8e1f → updated to f9101bc4index 6c4de3f92 source commits
f3300652The workbench pinned Flyology and Flyology HTTP to commits predating the 0.1.1
development line while requiring that line, so the pins named sources declaring
0.1.0-dev and had fallen 72 and 41 commits behind their branches. Both crates
are now published to the index for the required line, leaving the pins with
nothing to contribute but the contradiction.
Solution: Resolve both dependencies from the index
Drop the commit pins so the required development line selects the sources, and
the workbench tracks them the way every other dependent does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
f9101bc4Each SQL crate pins its siblings by path for local development and leaves the
matching dependencies declared as "*", because the pin supplies the version.
Local builds resolve through the pins and never notice. A path cannot cross
into the index, so anything depending on these crates from outside the
repository is free to resolve any version at all.
Solution: Declare the versions the pins stand in for
Constrain each pinned dependency to the development line its pinned checkout
provides, so the same versions are selected once the pins are gone. Local
resolution is unchanged, since a pin still takes precedence.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.1.1-devVersioned PostgreSQL raw SQL parser and Ada syntax tree
92212697 → updated to a8af8e1findex 510cfcf61 source commit
a8af8e1fThe native PostgreSQL parser and generated owned AST support five grammar versions, but CI does not exercise the upstream regression corpus. Parser generator, scanner, semantic-action, and conversion defects can therefore survive the existing focused cases.
Solution: compare every PostgreSQL regression file against pinned oracles
Acquire and checksum the exact upstream corpora, recover oracle-valid statements from psql-oriented and expected-error files, compare native and owned AST layers, cache the inputs in CI, and correct the generator/runtime defects exposed across PostgreSQL 14 through 18.
a8af8e1f → updated to f9101bc4index 6c4de3f92 source commits
f3300652The workbench pinned Flyology and Flyology HTTP to commits predating the 0.1.1
development line while requiring that line, so the pins named sources declaring
0.1.0-dev and had fallen 72 and 41 commits behind their branches. Both crates
are now published to the index for the required line, leaving the pins with
nothing to contribute but the contradiction.
Solution: Resolve both dependencies from the index
Drop the commit pins so the required development line selects the sources, and
the workbench tracks them the way every other dependent does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
f9101bc4Each SQL crate pins its siblings by path for local development and leaves the
matching dependencies declared as "*", because the pin supplies the version.
Local builds resolve through the pins and never notice. A path cannot cross
into the index, so anything depending on these crates from outside the
repository is free to resolve any version at all.
Solution: Declare the versions the pins stand in for
Constrain each pinned dependency to the development line its pinned checkout
provides, so the same versions are selected once the pins are gone. Local
resolution is unchanged, since a pin still takes precedence.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.1.1-devShared native runtime for versioned Flyology PostgreSQL parsers
92212697 → updated to a8af8e1findex 510cfcf61 source commit
a8af8e1fThe native PostgreSQL parser and generated owned AST support five grammar versions, but CI does not exercise the upstream regression corpus. Parser generator, scanner, semantic-action, and conversion defects can therefore survive the existing focused cases.
Solution: compare every PostgreSQL regression file against pinned oracles
Acquire and checksum the exact upstream corpora, recover oracle-valid statements from psql-oriented and expected-error files, compare native and owned AST layers, cache the inputs in CI, and correct the generator/runtime defects exposed across PostgreSQL 14 through 18.
a8af8e1f → updated to f9101bc4index 6c4de3f92 source commits
f3300652The workbench pinned Flyology and Flyology HTTP to commits predating the 0.1.1
development line while requiring that line, so the pins named sources declaring
0.1.0-dev and had fallen 72 and 41 commits behind their branches. Both crates
are now published to the index for the required line, leaving the pins with
nothing to contribute but the contradiction.
Solution: Resolve both dependencies from the index
Drop the commit pins so the required development line selects the sources, and
the workbench tracks them the way every other dependent does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
f9101bc4Each SQL crate pins its siblings by path for local development and leaves the
matching dependencies declared as "*", because the pin supplies the version.
Local builds resolve through the pins and never notice. A path cannot cross
into the index, so anything depending on these crates from outside the
repository is free to resolve any version at all.
Solution: Declare the versions the pins stand in for
Constrain each pinned dependency to the development line its pinned checkout
provides, so the same versions are selected once the pins are gone. Local
resolution is unchanged, since a pin still takes precedence.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.1.1-devNative PostgreSQL 14 parser and owned Ada AST
92212697 → updated to a8af8e1findex 510cfcf61 source commit
a8af8e1fThe native PostgreSQL parser and generated owned AST support five grammar versions, but CI does not exercise the upstream regression corpus. Parser generator, scanner, semantic-action, and conversion defects can therefore survive the existing focused cases.
Solution: compare every PostgreSQL regression file against pinned oracles
Acquire and checksum the exact upstream corpora, recover oracle-valid statements from psql-oriented and expected-error files, compare native and owned AST layers, cache the inputs in CI, and correct the generator/runtime defects exposed across PostgreSQL 14 through 18.
a8af8e1f → updated to f9101bc4index 6c4de3f92 source commits
f3300652The workbench pinned Flyology and Flyology HTTP to commits predating the 0.1.1
development line while requiring that line, so the pins named sources declaring
0.1.0-dev and had fallen 72 and 41 commits behind their branches. Both crates
are now published to the index for the required line, leaving the pins with
nothing to contribute but the contradiction.
Solution: Resolve both dependencies from the index
Drop the commit pins so the required development line selects the sources, and
the workbench tracks them the way every other dependent does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
f9101bc4Each SQL crate pins its siblings by path for local development and leaves the
matching dependencies declared as "*", because the pin supplies the version.
Local builds resolve through the pins and never notice. A path cannot cross
into the index, so anything depending on these crates from outside the
repository is free to resolve any version at all.
Solution: Declare the versions the pins stand in for
Constrain each pinned dependency to the development line its pinned checkout
provides, so the same versions are selected once the pins are gone. Local
resolution is unchanged, since a pin still takes precedence.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.1.1-devNative PostgreSQL 15 parser and owned Ada AST
92212697 → updated to a8af8e1findex 510cfcf61 source commit
a8af8e1fThe native PostgreSQL parser and generated owned AST support five grammar versions, but CI does not exercise the upstream regression corpus. Parser generator, scanner, semantic-action, and conversion defects can therefore survive the existing focused cases.
Solution: compare every PostgreSQL regression file against pinned oracles
Acquire and checksum the exact upstream corpora, recover oracle-valid statements from psql-oriented and expected-error files, compare native and owned AST layers, cache the inputs in CI, and correct the generator/runtime defects exposed across PostgreSQL 14 through 18.
a8af8e1f → updated to f9101bc4index 6c4de3f92 source commits
f3300652The workbench pinned Flyology and Flyology HTTP to commits predating the 0.1.1
development line while requiring that line, so the pins named sources declaring
0.1.0-dev and had fallen 72 and 41 commits behind their branches. Both crates
are now published to the index for the required line, leaving the pins with
nothing to contribute but the contradiction.
Solution: Resolve both dependencies from the index
Drop the commit pins so the required development line selects the sources, and
the workbench tracks them the way every other dependent does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
f9101bc4Each SQL crate pins its siblings by path for local development and leaves the
matching dependencies declared as "*", because the pin supplies the version.
Local builds resolve through the pins and never notice. A path cannot cross
into the index, so anything depending on these crates from outside the
repository is free to resolve any version at all.
Solution: Declare the versions the pins stand in for
Constrain each pinned dependency to the development line its pinned checkout
provides, so the same versions are selected once the pins are gone. Local
resolution is unchanged, since a pin still takes precedence.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.1.1-devNative PostgreSQL 16 parser and owned Ada AST
92212697 → updated to a8af8e1findex 510cfcf61 source commit
a8af8e1fThe native PostgreSQL parser and generated owned AST support five grammar versions, but CI does not exercise the upstream regression corpus. Parser generator, scanner, semantic-action, and conversion defects can therefore survive the existing focused cases.
Solution: compare every PostgreSQL regression file against pinned oracles
Acquire and checksum the exact upstream corpora, recover oracle-valid statements from psql-oriented and expected-error files, compare native and owned AST layers, cache the inputs in CI, and correct the generator/runtime defects exposed across PostgreSQL 14 through 18.
a8af8e1f → updated to f9101bc4index 6c4de3f92 source commits
f3300652The workbench pinned Flyology and Flyology HTTP to commits predating the 0.1.1
development line while requiring that line, so the pins named sources declaring
0.1.0-dev and had fallen 72 and 41 commits behind their branches. Both crates
are now published to the index for the required line, leaving the pins with
nothing to contribute but the contradiction.
Solution: Resolve both dependencies from the index
Drop the commit pins so the required development line selects the sources, and
the workbench tracks them the way every other dependent does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
f9101bc4Each SQL crate pins its siblings by path for local development and leaves the
matching dependencies declared as "*", because the pin supplies the version.
Local builds resolve through the pins and never notice. A path cannot cross
into the index, so anything depending on these crates from outside the
repository is free to resolve any version at all.
Solution: Declare the versions the pins stand in for
Constrain each pinned dependency to the development line its pinned checkout
provides, so the same versions are selected once the pins are gone. Local
resolution is unchanged, since a pin still takes precedence.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.1.1-devNative PostgreSQL 17 parser and owned Ada AST
92212697 → updated to a8af8e1findex 510cfcf61 source commit
a8af8e1fThe native PostgreSQL parser and generated owned AST support five grammar versions, but CI does not exercise the upstream regression corpus. Parser generator, scanner, semantic-action, and conversion defects can therefore survive the existing focused cases.
Solution: compare every PostgreSQL regression file against pinned oracles
Acquire and checksum the exact upstream corpora, recover oracle-valid statements from psql-oriented and expected-error files, compare native and owned AST layers, cache the inputs in CI, and correct the generator/runtime defects exposed across PostgreSQL 14 through 18.
a8af8e1f → updated to f9101bc4index 6c4de3f92 source commits
f3300652The workbench pinned Flyology and Flyology HTTP to commits predating the 0.1.1
development line while requiring that line, so the pins named sources declaring
0.1.0-dev and had fallen 72 and 41 commits behind their branches. Both crates
are now published to the index for the required line, leaving the pins with
nothing to contribute but the contradiction.
Solution: Resolve both dependencies from the index
Drop the commit pins so the required development line selects the sources, and
the workbench tracks them the way every other dependent does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
f9101bc4Each SQL crate pins its siblings by path for local development and leaves the
matching dependencies declared as "*", because the pin supplies the version.
Local builds resolve through the pins and never notice. A path cannot cross
into the index, so anything depending on these crates from outside the
repository is free to resolve any version at all.
Solution: Declare the versions the pins stand in for
Constrain each pinned dependency to the development line its pinned checkout
provides, so the same versions are selected once the pins are gone. Local
resolution is unchanged, since a pin still takes precedence.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.1.1-devNative PostgreSQL 18 parser and owned Ada AST
92212697 → updated to a8af8e1findex 510cfcf61 source commit
a8af8e1fThe native PostgreSQL parser and generated owned AST support five grammar versions, but CI does not exercise the upstream regression corpus. Parser generator, scanner, semantic-action, and conversion defects can therefore survive the existing focused cases.
Solution: compare every PostgreSQL regression file against pinned oracles
Acquire and checksum the exact upstream corpora, recover oracle-valid statements from psql-oriented and expected-error files, compare native and owned AST layers, cache the inputs in CI, and correct the generator/runtime defects exposed across PostgreSQL 14 through 18.
a8af8e1f → updated to f9101bc4index 6c4de3f92 source commits
f3300652The workbench pinned Flyology and Flyology HTTP to commits predating the 0.1.1
development line while requiring that line, so the pins named sources declaring
0.1.0-dev and had fallen 72 and 41 commits behind their branches. Both crates
are now published to the index for the required line, leaving the pins with
nothing to contribute but the contradiction.
Solution: Resolve both dependencies from the index
Drop the commit pins so the required development line selects the sources, and
the workbench tracks them the way every other dependent does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
f9101bc4Each SQL crate pins its siblings by path for local development and leaves the
matching dependencies declared as "*", because the pin supplies the version.
Local builds resolve through the pins and never notice. A path cannot cross
into the index, so anything depending on these crates from outside the
repository is free to resolve any version at all.
Solution: Declare the versions the pins stand in for
Constrain each pinned dependency to the development line its pinned checkout
provides, so the same versions are selected once the pins are gone. Local
resolution is unchanged, since a pin still takes precedence.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.1.1-devAda-native QUIC transport for Flyology tasks
ddbba90d → updated to d99490cfindex 510cfcf63 source commits
1802f1c9SPARK proof regressions and Autobahn conformance failures can go unnoticed because CI does not execute those campaigns. The existing nightly schedule covers other qualifications but not these policy and protocol checks.
Solution: Gate proofs and schedule Autobahn qualification
Run the complete QUIC and HTTP proof suite for every CI trigger and add a scheduled matrix for all maintained Autobahn profiles and task lanes, including matched OpenSSL provenance for secure profiles.
3d0df29fThe pull request proof job twice exhausts its level-one prover budget on QUIC ACK range obligations, even though the same obligations pass locally. This leaves an otherwise healthy CI run red and makes proof coverage unreliable on hosted runners.
Solution: Give CI level-two proof effort
Let both proof scripts accept FLYOLOGY_PROOF_LEVEL while preserving level one as the default for manual runs. Set the CI proof job to level two so hosted provers have enough effort to complete the full QUIC and HTTP campaigns.
d99490cfThe benchmark runner assumes Flyology runtime scripts live in the HTTP project root. Separate Codex worktrees and Alire dependency layouts do not provide that path, so runtime preparation fails before the benchmark can build.
Solution: Prepare the runtime from the resolved Flyology root
Resolve Flyology through the shared dependency-root helper and use that root when invoking prepare-rts.sh, preserving explicit, adjacent monorepo, and Alire-provided dependency layouts.
d99490cf → updated to 439a9fb6index 6e2db0ef30 source commits
24c1ea29Servers must currently mutate route setup state directly and cannot safely publish coordinated handler, policy, and middleware changes while requests are in flight. Callers also lack stable identities for addressing registered routes and middleware.
Solution: Publish immutable router generations through stable identities
Add opaque route and middleware IDs, transactional update operations, stale-writer detection, and single-load atomic generation publication. Exercise replacement and concurrent dispatch behavior, document the runtime and memory tradeoffs, and add the workflow to the server guide.
5e5aae59Every dispatch loads the published generation and then follows a second pointer to reach its immutable configuration. Each successful update also allocates a separate generation holder solely for retention bookkeeping.
Solution: Publish configuration allocations directly
Link retained configurations through their own private history field and atomically publish the configuration address. This removes the holder allocation and one dependent request-path load without changing stale-update or lifetime behavior.
1df8c3a6The retention chain, the publication gate, and the update base all carry
System.Address, so recovering a configuration needs an unchecked conversion
from an address-conversion pointer into a pool-specific access type. Router
initialization also allocates its first configuration in the declarative
part, where the handler written to release a failed allocation cannot see
it: an exception raised there propagates without reaching the handler.
Solution: Link retained configurations through a typed access
Complete the configuration type from an incomplete declaration and make its
access type general, so Previous, First_Configuration, the update base, and
the publication gate all hold access values and the unchecked conversion
disappears. The address round trip now stays on the published atomic word
alone. Allocate the first configuration in the statement part so its handler
covers the allocation it was written for.
38b98060Add, Add_Middleware, Add_Route_Middleware, Mount, and the two direct setters
write the currently published configuration in place, and they admit the new
element before they write it: the count is incremented first. A request that
races such a registration reads the raised count and then a route with a null
handler or a middleware entry with a null component, so the pipeline raises
Constraint_Error. Only the two setters carried a setup-only note, and the
identity-returning registration overloads carried none.
Solution: Seal the router at its first dispatch
Publish a seal word from the first dispatch and refuse every direct
registration and setter afterwards with Route_Error naming the update path.
The dispatch cost is one acquire load of a clean shared line, since the store
happens only while the word is still clear. Fill each route and middleware
slot before the count admits it so the ordering is sound on its own, and
state the sealing rule on the router type and the affected operations.
1fd5ed9bMount assigns a fresh identity to every middleware entry it copies into the
mounted routes, so the identity the application holds for a subrouter's
component matches nothing in the destination. Remove_Middleware and
Replace_Middleware then raise while the mounted copies keep running the old
component, even though both already walk every route chain accumulating
matches, which only makes sense when one identity can appear in several
chains. Begin_Update also accepted a mounted generation, and two candidate
setters skipped the mounted guard the other mutators apply.
Solution: Carry the source registration identity through a mount
Copy mounted middleware entries verbatim so one identity names every chain
that carries the registration. Refuse Begin_Update on a mounted generation,
which covers every candidate mutator in one place, and add the guard to the
admission and challenge setters that lacked it. State the mounting rule on
the identity type, on Remove_Middleware, and in the server guide.
8244e2fbCommit raises Stale_Update while the candidate is still attached, so
Begin_Update on that object then raises Program_Error. The documented retry
loop only works because it declares the update inside the loop; hoisting it,
which is the obvious way to avoid re-entering the declaration, deadlocks the
caller into an unusable object. There is also no way to give up on a
candidate that failed validation.
The concurrency audit could not fail cleanly either. Its workers park on a
barrier that a failure before Start never opens, and the join waits for all
four to finish with no bound, so a setup error hangs the audit until the
harness timeout instead of reporting. Its assertion also could not tell a
run that spanned a publication from one that never observed a change.
Solution: Release a stale candidate and bound the audit
Release the candidate when Commit rejects it as stale, since its base is gone
and it can never be published, and leave a validation failure active so the
caller can correct and commit it. Add Abandon for an explicit discard.
Rebuild the audit around a cancellable barrier, bounded joins, and a fixed
publication count chosen odd so the closing request of every worker has one
correct answer. Workers now record a pre-publication and a post-publication
observation, so the test proves it spanned a generation change. Cover the
duplicate-name and ambiguity rejections that candidate Add defers to Commit,
and the Set_Match, Rename, Set_Middleware_Stage, and admission operations
that had no coverage at all.
5e9b0b41Every introspection operation takes its own acquire load, so a caller that
reads Route_Count and then describes each index reads two generations. A
commit that removes a route between the two calls makes the last index
invalid and Describe_Route raises Constraint_Error. The same split affects
Route_Middleware_Count with Describe_Route_Middleware. A diagnostics endpoint
enumerating routes therefore fails intermittently on exactly the deployments
runtime reconfiguration is for, and nothing lets a caller pin a generation.
Solution: Add a snapshot that pins one generation
Add a Snapshot that captures the published generation, with the whole
introspection surface available on it. Route the Router forms and the
Snapshot forms through shared configuration-level operations so both answer
identically. Retained generations already outlive any snapshot, so the
snapshot only holds a reference; its finalization is the hook a later
reclamation scheme needs.
b345dc6fCommit links every superseded configuration into the router's retention
chain, and only router finalization frees it. Retention is what keeps an
in-flight dispatch from reading freed storage, but nothing bounds it and
nothing reports it: a control plane that commits periodically grows the
chain for the whole process lifetime with no way to observe the growth and
no way to release it short of destroying the router.
Solution: Report retained generations and release them on demand
Add Retained_Generations so the growth is measurable, and Reclaim to release
every superseded generation while keeping the published one. Reclaim states
its precondition plainly: the caller must know that no dispatch is running
and no snapshot of a superseded generation is live, and that is not checked.
This keeps the request path free of reader accounting while giving a drained
server a way to return the memory.
e0a9e1b8Registration compiles each pattern into a Segment_List so dispatch does not
split it per request, but the ambiguity checks throw that away. Patterns
Overlap and Specificity both take a String and split it again, so Check_Add
re-splits every stored pattern on every registration and Commit re-splits
both sides of every route pair. Add also computed the same split twice, once
for storage and once for the score. The name-keyed Add_Route_Middleware
scanned for the name, took an identity, then re-read the router and scanned
again for that identity, which can report an identity failure for a name
that does resolve. Name comparisons allocated a String per route inspected.
Solution: Compare the segments and score each route already carries
Take a Segment_List in Patterns_Overlap and Specificity, so both the
registration check and the commit check read the stored segments and the
stored score. Order the cheap score comparison before the segment walk.
Compile each pattern once per Add. Resolve the name-keyed middleware
overload in one pass over one generation. Compare names against the stored
Unbounded_String instead of converting each one.
Cover the result with an oracle: for each of eight pattern pairs, assert the
registration decision and the commit decision are both the expected verdict.
bdcfbc8cThe guide still tells applications to bound their update rate when retained
memory matters, which describes a rate tradeoff rather than growth the
router now reports and can release. It states that direct registration must
not run concurrently with dispatch without saying the router enforces that.
It describes per-call introspection as a snapshot, which is what the new
Snapshot type actually provides, and it tells a caller to rebuild a stale
candidate without saying the candidate is already released.
Solution: Describe sealing, snapshots, and generation retention
Replace the retention paragraph with Retained_Generations and Reclaim, and
state Reclaim's unchecked precondition beside the call. Say that the first
dispatch seals the router and what a later direct registration raises. Add a
short section for traversing one generation with Take_Snapshot, and record
that Stale_Update releases its candidate while a validation failure keeps
one for correction. Every new API mention links its generated GNATdoc entry,
and all 286 generated-API links on the hand-written pages resolve.
799995fdRouter_Configuration is a large non-limited discriminated record, so RM 6.2
leaves by-copy or by-reference to the implementation for an in parameter.
Dispatch_Configuration takes a generation that way, and so does every
introspection and validation operation that reads one. GNAT passes them by
reference and the request-path measurements show no copy, but nothing in the
language requires it. An implementation that chose by-copy would duplicate a
whole generation, with every route pattern, segment list, and middleware
name, on each request.
Solution: Take every generation parameter explicitly aliased
An explicitly aliased parameter is passed by reference by definition, so
mark all fourteen Router_Configuration parameters aliased. Every actual is
already an aliased view reached through a Configuration_Access, so no call
site changes. The guarantee is now a property of the declaration rather than
of one compiler's choice.
977add4aThe published-generation word a dispatch reads and the word a commit writes
live in the same router object, so a reload can invalidate the line the
dispatchers are reading. Nothing measures that. The existing benchmark
servers run over sockets and an external load generator, where connection
and syscall cost hides an effect this small, and none of them reload the
router while traffic runs.
Solution: Add an in-process router reload benchmark
Drive routed dispatch from worker tasks over an allocation-free in-memory
transport, with one loader task republishing the configuration at a chosen
rate, and sweep that rate against otherwise identical traffic. Each worker
counts on its own stack and reports once at the end, so the harness adds no
contention of its own to the thing being measured.
The runner repeats each rate as independent process runs and reports best,
median, worst, and spread, because code layout changes per link and process
placement dominates repetition inside one run. Reporting the spread is the
point: an effect smaller than it cannot be claimed from this harness.
926fed4bEvery dispatch reads the published-generation word and the seal word. Both
sit in the same record as First_Configuration and the publication gate,
which only a commit touches, and nothing in the declaration separates the
two groups. Whatever layout the compiler happens to produce today is a
consequence of field order and padding, not a property anyone stated.
Solution: Place the two dispatch-read words in one interference region
Group them with Flyology_Cachelines.Padded_Groups, so a dispatch fetches one
region rather than two and a commit dirties the readers' region rather than
whatever else landed beside it. The two words have the compatible ownership
that a group requires: each is read by every dispatch and written only by a
commit.
This is not a measured improvement and must not be read as one. A commit
dirties the readers' region once, not once per dispatch, so the cost follows
the reload rate rather than the traffic: between 10 and 100 reloads per
second it is 0.0004% to 0.004% of throughput, four orders of magnitude below
what the reload benchmark can separate from run-to-run spread. The change is
made because the layout is then correct by declaration instead of by
accident. The benchmark now records what it can and cannot resolve, so the
question is not reopened on the assumption that a measurement is available.
8cae2c89The HTTP/2 qualification job installs its independent peer with a bare
sudo apt-get update, and no step in either workflow bounds its own runtime.
When the mirror stops answering, apt-get does not fail; it waits. The step
waits with it, and the only thing that eventually ends the run is the job
timeout, or a person noticing.
On 2026-08-19 that happened three times. Run 32226557711 on main sat on the
update for the whole 150-minute job budget before the job timeout ended it,
with nobody watching. Runs 32278682887 and 32294697524 sat there 83 and 64
minutes before someone cancelled them by hand. The same job finishes in 8 to
14 minutes when the mirror answers, and the install itself takes 11 seconds
at the median, so each of those runs spent between four and eleven times the
job's whole healthy duration waiting on one command. A run that hangs also
reads as a run that is working: there is no failure to retry, and the
concurrency group keeps the branch occupied until someone intervenes.
Solution: Bound every network step, and retry the update that stalls
Give each step that reaches the network a timeout-minutes bound, sized
against the slowest healthy observation over the preceding 60 runs rather
than against a guess, so a step that trips one has stalled rather than run
slowly. Checking out sources and configuring the index finish in seconds and
are given 5 minutes; the peer install runs 11 seconds at the median and 5
minutes at its slowest and is given 15. Installing Alire is given 15 on
different grounds: every observation of it is a cache hit, so the
observations bound the cached path and say nothing about a cold toolchain
download.
Wrap apt-get update in three attempts, each bounded on its own by timeout,
because the step bound alone would convert a stall into a failure but not
into a recovery. A wedged mirror connection now becomes a retry against a
fresh one, so a single transient failure does not fail the job, and the step
bound stays a backstop rather than the mechanism that catches the common
case. Only the update is retried: it is the command that stalled, and it is
the one here that is safe to interrupt and repeat, since a killed run leaves
partial package lists that the next run refetches. Retrying apt-get install
would risk resuming from a half-configured dpkg state to no purpose.
The same failure mode existed in two other places and is closed the same
way. The Pages workflow had no job-level timeout at all, so a stall there ran
to the six-hour GitHub default; both its jobs now carry one, and its fetch
and install steps carry step bounds. The h3spec download in the HTTP/3
conformance script used a bare curl -fL, which hangs on a stalled connection
exactly as apt-get did; it now retries with a per-attempt time limit, which
the existing checksum makes safe, because a truncated or resumed body never
survives it.
Nothing about what is installed or how anything is built or tested changes.
On a healthy run every bound is untouched and every command runs as before;
the bounds only decide how a stalled run ends.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
27a8ee59flyology_iri_benchmark times can_parse and parse_href over the pinned URL corpus. Neither reaches a component getter: can_parse constructs nothing, and parse_href consumes only the stored serialization length. The paths that read components back out of an already parsed reference, and Resolve above them, have no benchmark at all, so a change to them can be argued only from reading the code.
Solution: Add a flyology_bench harness for those paths
flyology_iri_resolve_benchmark reads all eight components at three reference lengths and resolves the RFC 3986 section 5.4 references against a short and a long base. It carries its own inputs, so unlike benchmark.sh it needs neither the Ada URL checkout nor the pinned corpus. Batches fold a value derived from each result into an accumulator handed to a barrier after the ending timestamp, and each iteration advances to a different input.
Two builds of one library cannot share a process, so before/after runs join through Flyology_Bench.Baselines, whose fingerprint refuses a comparison across operating system, architecture, compiler, or build mode while deliberately excluding the library revision. scripts/resolve-benchmark.sh selects the release mode the published medians measure and passes it to the binary for that fingerprint. Diagnose and Parse do coexist, so the validation pair is a real paired Compare.
Requested axes are Process_Resource_Metrics, the portable Darwin/Linux set; the metrics CSV reports each axis's collection status rather than leaving it assumed.
d45630c0Every public component getter reaches the stored serialization through the private Slice helper, which materialized the entire reference with To_String and then returned one range of it. The release objects show that call sited unconditionally and ahead of the zero-span test, so an absent component paid a whole-reference copy and a present one paid two allocations and two copies. Reading a four-character scheme out of a 233-byte IRI copied 233 bytes and then four. Resolve sits directly on top of that and reads Path four times and the base path twice. Remove_Dot_Segments, which runs on essentially every resolution, renormalized its input to one-based indexing through an Unbounded round trip.
Solution: Copy only the component, and read each path once
Slice now calls Ada.Strings.Unbounded.Slice, keeping the zero guard ahead of it because an absent component has First = 0 and that subprogram takes a Positive Low. The getters compile to a tail call to that subprogram on the present path and to an eight-byte bounds allocation on the absent one. The returned bounds do not change: GNAT's Slice returns the underlying Low .. High range, which is what the discarded whole-text copy was sliced by. Remove_Dot_Segments renormalizes with a constrained subtype instead of the heap. Resolve hoists the relative path and the base path into locals.
Release-mode medians on an Apple M3 Max, interleaved before/after/before/after against an unrelated build holding the load average near 25, with the repeated pre-change round as a drift control: reading all eight components falls 41.9% at 44 bytes, 33.5% at 235, and 63.8% at 2,009; resolution falls 22.2% against an 18-byte base and 15.5% against a 233-byte one. The drift control landed within 3% of its own first round. Process_RSS_Change and both page-fault axes read zero on both sides, as expected for churn the allocator reuses within a batch; wall time and thread CPU time are the axes that respond.
Component tests now name the absent components, since the zero guard is what keeps an absent span away from a Positive parameter.
4df3c149Resolve ends by parsing its assembled result into a second Reference, a heap serialization plus eight component spans, purely to establish that the bytes are well formed. A caller that stores resolved references as opaque bytes needs exactly that guarantee and none of the spans, and has no way to ask for it.
Solution: Add a serialization-returning Resolve
Diagnose establishes the same guarantee without constructing anything, and for URI and IRI it is the same computation Parse performs: both reduce to Analyze's error, and a successful non-web parse stores its input verbatim. Web mode normalizes, so there the normalized serialization is the result and the new form still parses to obtain it. The value equals Image of the Reference the other form returns, which the section 5.4 suite now asserts on every case it already covered.
The two forms share a private Assemble rather than one calling the other. Composing them the obvious way, with the Reference form parsing the serialization form's result, would make it validate twice and cost it roughly 210 ns it does not pay today.
Paired Compare in one process, release mode, against a 233-byte base: 821.3 ns/op returning a Reference against 743.4 ns/op returning a String, 9.45% less, 95% CI [1.102, 1.107], winning 100 of 100 sample pairs at an order effect of -0.46%. Isolating the validation step, Diagnose measures 211.2 ns/op against Parse's 270.1. The end-to-end saving exceeds that difference because the serialization form also skips constructing and finalizing the result Reference, not only the parse that fills it. The Reference form's median is unchanged, which is what confirms it kept its single validation.
cfb25efcResolve merges and reduces its result by appending to an Unbounded_String, which reallocates on nearly every append, and by calling Remove_Dot_Segments, which builds a second one and returns To_String of it. Segment removal is worse than that suggests: collapsing one "/../" runs a whole To_String followed by a Set_Unbounded_String. None of this is visible in the API, and all of it is on the path of every resolution.
Solution: Assemble into a String sized from the parsed references
Merging and dot-segment removal never produce more than the base, the relative reference, and the separators between them, so the serialized lengths of the two parsed references bound the result. Both are real lengths rather than Max_Length, which may be Positive'Last, and both come from parsed references rather than input bytes, because web normalization can expand a relative past what it was written with. A bound above what belongs on the stack takes one exact allocation instead, which keeps a single copy of the merge rather than a rarely exercised second implementation.
Release-mode paired medians against a 233-byte base: the Reference form falls from 823.2 to 550.4 ns and the serialization form from 743.4 to 466.5, so the change reaches the API every existing caller already uses. Resolution against an 18-byte base falls from 475.8 to 331.0. The public specification is untouched.
A procedure form writing into a caller-supplied buffer was prototyped against this and measured 4.7% below the serialization form, winning 69 of 100 pairs. That difference is the returned copy alone, which is all the wider API buys once the assembly no longer allocates, so it is not adopted.
616fdfd5Assembling into a buffer put four objects proportional to the reference length on the primary stack: the one-based copy of the dot-segment input, the merged path, the prefix taken out of the base path, and the result staged on the way out of the heap branch. The heap branch existed precisely so that a caller raising Max_Length would not put a large buffer on the stack, and then reached the stack anyway through the other three. Resolving a four megabyte base raised Storage_Error where the previous implementation returned a result. The heap branch also freed its buffer only on its two normal exits, so any exception through it leaked the allocation, and neither the bound nor the append guard was safe against wraparound in a body whose release profile suppresses overflow checks.
Solution: Keep every size-proportional object off the primary stack
Dot-segment reduction indexes its input through an offset rather than copying it one-based, so the copy is gone rather than moved. The prefix is named by bounds into the base path and written straight into the merged buffer. The merged path takes the same stack-or-heap fork as the assembly buffer, because section 5.2.4 reduces the joined path as one string and the pieces cannot be reduced separately. An extended return builds the result where the caller wants it. Both heap blocks free their allocation from an others handler as well as their normal exits.
The append guard subtracts rather than adds, so it cannot wrap, and the bound is computed in Long_Long_Integer and rejected unless it fits Natural with room to spare, which also keeps every later index lookahead inside range.
Resolving against bases of one hundred kilobytes, four megabytes and forty megabytes now all succeed, matching the previous implementation. A differential harness comparing both public forms against a verbatim reimplementation of the previous assembly reports no divergence over 429,208 cases, of which 105,202 raise on both sides.
18ecffdbResolve returning String and Resolve returning Reference differ only in what they return. Most calls select between them from context, but a call whose context supplies no type does not: comparing two resolutions with "=", or taking an attribute of one, becomes ambiguous. Both were legal while only the Reference form existed, so an existing caller of the published crate meets a compiler error whose cause is not apparent from the code in front of it.
Solution: Say so where the operation is documented
Record the overload, name the two shapes that stop compiling, and give the disambiguation. A qualified expression settles either in place, and naming the result in a constant always works. Keeping the shared name is deliberate: the failure is a compile error rather than a silent change of meaning, and the fix is local to the call.
2f3df5f8BENCHMARKS.md carries medians taken before the assembly stopped staging size-proportional locals on the stack, and its comparison between the two assemblies stitches together runs from separate sittings on a host under varying load.
Solution: Re-measure both assemblies in one interleaved sitting
Swap the two assemblies in place across four rounds, first arm repeated as a drift control, and report the resolution rows from that. The control landed within 0.4% of itself. Removing the one-based copy from dot-segment reduction moved the figures slightly in its favour, so the buffer assembly now reads 34% below the Unbounded one for both public forms rather than 33%.
4b7b1d6cCan_Parse and Diagnose report whether input parses but not how it
classifies, so a caller that admits only absolute references -- an RDF
crate deciding whether bytes are an absolute IRI, say -- has to reach for
Try_Parse. Try_Parse builds a Reference, and building one stores a copy
of a serialization that is byte for byte the input the caller already
holds. The caller reads Kind and drops it.
Solution: Report the classification without building the reference
Add Inspect, which runs the analysis Try_Parse runs and reports the
structural kind alongside the parse error. Analyze works on spans over
the caller's String, so nothing is stored and nothing is allocated. The
differential harness now holds Inspect to Try_Parse on both the error and
the kind across the whole seeded corpus.
Admitting "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" as an
absolute IRI:
allocs bytes ns
via Try_Parse 1 80 145
via Inspect 0 0 103
-100% -100% -29%
Measured with FLYOLOGY_IRI_BUILD_MODE=release. Both forms were run
alternately, eight rounds each, and the minimum taken, because the host
carries unrelated build load. Allocation counts are exact.
bc3e3217Preprocess removes tab, newline and carriage return by appending the
surviving bytes to an Unbounded_String one at a time. Almost no URL holds
any of them, so the ordinary case takes an accumulator, grows it, and
returns a verbatim copy of its own input.
Solution: Return the trimmed slice when there is nothing to remove
Scan first and build only once a byte has to be dropped, which also keeps
the controlled accumulator out of the common path entirely. The slice is
slid to one because Input_Offset and the parser below read the result at
one-based offsets.
Absolute web URLs that take the WHATWG pipeline rather than the fast
path:
allocs bytes ns
web_slow, before 25 1008 2072
web_slow, after 23 912 1964
-8% -10% -5%
web_idna, before 16 752 1661
web_idna, after 14 656 1547
-13% -13% -7%
Measured with FLYOLOGY_IRI_BUILD_MODE=release. Revisions were built into
separate worktrees and run alternately, eight rounds each, minimum taken,
because the host carries unrelated build load. Allocation counts are
exact.
WPT URL conformance stays at 919/919 with no mismatches.
bf79ef49Encode, Encode_Opaque_Path and Percent_Decode each accumulate into an
Unbounded_String one character at a time and then copy the whole thing
out with To_String. That is a heap block, the reallocations that growing
it byte by byte implies, and one full copy, for a result whose length is
a pure function of the input. Two of the callers encode a single
character at a time inside a loop, so each iteration paid all of it.
Solution: Count the result, then write it into the returned object
Each function now measures its output in a first pass and fills an
extended return object in a second. Since the count is exact, the
returned String is the only storage involved -- no accumulator and no
working buffer, so nothing is allocated and nothing is copied.
Sizing a buffer up front was the alternative, but the bound that is known
a priori is three bytes per input byte, and with Max_Length at one
mebibyte that reserves three mebibytes on the stack in a crate that does
not build with -fstack-check. Counting first costs a second pass over the
input and reserves exactly what the result needs.
Absolute web URLs that take the WHATWG pipeline rather than the fast
path:
allocs bytes ns
web_slow, before 23 912 1964
web_slow, after 17 720 1670
-26% -21% -15%
web_idna, before 14 656 1547
web_idna, after 12 592 1449
-14% -10% -6%
Measured with FLYOLOGY_IRI_BUILD_MODE=release. Revisions were built into
separate worktrees and run alternately, eight rounds each, minimum taken,
because the host carries unrelated build load. Allocation counts are
exact and do not depend on any of that.
The corpus benchmark does not move: over the pinned 100,025 URLs,
can_parse holds at 22 ns minimum and parse_href at 105 against 103, with
acceptance identical at 99999. That corpus is dominated by URLs taking
the allocation-free fast path, which never reach either function.
WPT URL conformance stays at 919/919 with no mismatches, which is the
corpus that exercises these two functions hardest.
82106435Since "The router's dispatch-read words share a region with writer state",
routing.ads groups its two dispatch-read words with
Flyology_Cachelines.Padded_Groups. That spec is one of the files the
documentation build documents, and GNATdoc walks every project in the
closure that Documentation.Excluded_Project_Files does not name, so the
private with dragged the cachelines crate in with it. Its two tagged
types, Fitted_Groups.Grouped_Array and Padded_Groups.Grouped_Array, then
appeared in the index's tagged-type section, each linked to a page of its
own -- and GNATdoc writes a page per tagged type only under
--html-oop-style, which this build does not pass. This crate's own tagged
types are linked as unit page plus fragment and resolve; those two never
could.
The site check reports both as missing targets, so the Pages workflow has
failed on every push to main since that commit landed and the published
site is frozen at the last green deploy. CI is unaffected: the two
workflows fail independently, and only one of them was ever red.
Solution: Document this crate and nothing else
Name every dependency in Excluded_Project_Files, beside the Flyology
runtime that was already there. Excluding cachelines alone would clear the
two broken links, but the closure walk is why they were reachable at all,
and the same walk had already put twenty-four pages of QUIC and allocator
internals into a reference whose own footer reads "Public HTTP API only".
The index now lists this crate's sixty-two entries, the search index
carries 2441 names with none from a dependency, and no page outside
Flyology.HTTP is written.
Removing those pages breaks no link. Their only inbound references were
the index entries that go away with them: scanning every website page and
every generated page for their file names finds nothing else pointing at
them.
Each exclusion names its project through the prefix Alire exports for that
crate rather than a path under FLYOLOGY_ROOT. The Flyology crate vendors
copies of the cachelines and allocator sources, so the two resolve to
different build directories, and GNATdoc rejects an excluded file that is
not the project it loaded -- "unable to resolve project file path", a hard
error rather than a silent no-op, so naming the vendored copy fails the
run outright. A bare project name fails the same way.
Verified with docs/api deleted first, since the workflow builds from a
fresh checkout and a stale page left behind by an earlier run would hide a
link the workflow cannot resolve. scripts/build-site.sh, which is what the
workflow runs, reports "Site check passed: 70 HTML files and 136 total
files".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
83db8c4dThe dual-stack overload of Routing.Serve divides HTTP_3_Capacity between the
address families, so HTTP_3_Capacity => 8 gave each listener four connection
slots rather than eight. Both client profiles in this test address
"localhost", which resolves to every loopback family on some hosts, and the
client's connection race completes a full QUIC handshake on each family
before it discards the losing lane. Each family therefore admits up to five
concurrent connections here: two live client profiles, two losing race lanes
awaiting release, and the raw Initial. Instrumented runs reach four of four
slots routinely.
A listener whose registry is full discards an Initial without answering it,
and no handshake-space probe timeout retransmits one. The raw handshake loop
compounded that: its receive raised Flyology.IO.Timeout_Error straight out of
the loop, so the eight attempts were never a retry budget and the first quiet
interval ended the run. The response loop had the same shape. Sizing a run of
twenty at each capacity reproduced the failure in nineteen runs at two slots
per family, seven at three, and one at four, tracking the discarded Initials
one for one.
Solution: Retransmit the handshake flight and size the listener for the load
Retain the last flight sent to the server and resend it when a receive
expires, spending the attempt budget on eight two-second probes rather than
one ten-second wait. The response loop keeps its budget for waiting, because
the server does drive application-space probe timeouts and retransmits on its
own; sixteen one-second probes stay inside the server's connection age.
Neither loop now ends an exchange on a single discarded datagram.
Raise HTTP_3_Capacity to sixteen and record the actual per-family model in
the comment, so the test stops running its listeners at full occupancy by
accident. With the retransmission in place the capacity is no longer load
bearing: at two slots per family, where the previous loop failed nineteen
runs of twenty, the raw handshake now completes in all twenty.
This is a test change. The absent handshake-space probe timeout is a real gap
in the QUIC layer and is not addressed here.
d7d87344CONNECTION_CLOSE is only reachable through the application space. An endpoint
that abandons a connection while it still holds Initial or Handshake keys has
no way to say so, and Build_Application_Close_Datagram's precondition rules it
out.
The receiving side is missing too. Initial_Space and Handshake_Space parse a
peer Transport_Close frame, count it, and discard it. A peer that did somehow
send one would be ignored, so adding a builder alone would change nothing
observable.
Solution: Report a peer close from both spaces and expose a close builder
Initial_Space and Handshake_Space now carry Peer_Closed and Close_Error in
their Process_Result and stop parsing at the frame, since RFC 9000 10.2.2 puts
the receiver into the draining state and the rest of the packet cannot change
that. Connection_Driver acts on it exactly as it already does for the
application space: Peer_Closed state, Connection_Closed status.
Build_Handshake_Close_Datagram builds the outbound half in the highest space
the connection holds, Handshake once that space exists and Initial otherwise,
so a peer that reached either level can read it. It carries the transport
APPLICATION_ERROR code that RFC 9000 10.2.3 reserves for an application close
that its encryption level cannot express, and leaves the connection Failed
because an endpoint that has begun closing must send nothing else.
Initial_Space.Build_Transport_Close_Packet passed a zero minimum packet length
for both roles, so a client-sent Initial close fell under 1,200 bytes and RFC
9000 14.1 requires a server to discard it. It now derives the minimum from the
role, as Build_Crypto_Packet already did. That also corrects the existing
client Fail_Initial_With_Close path, whose closes were unreadable for the same
reason.
c041b36dFor HTTP/3 both lanes of the address-family race run Start_Connection, which
completes a full QUIC handshake before Race.Choose picks a winner. The loser
raises Connection_Race_Lost, Close_Candidate runs, and Dispose_Connection sends
an application CONNECTION_CLOSE only when the transport reached the connected
state. A lane cancelled mid-handshake never did, so the client closes its UDP
socket and says nothing.
The server is left waiting for a handshake that will never arrive. On a bounded
listener that costs a connection slot for the whole handshake timeout, and a
peer that repeatedly starts and abandons handshakes can hold every slot.
Solution: Send a handshake-space close from the abandoned lane
Dispose_Connection now picks the close by transport state rather than skipping
it: an application close when connected, and Build_Handshake_Close_Datagram
when the connection is still in Client_Initial or Client_Handshake. Both go out
on the connection's own UDP socket before it closes, and both remain best
effort, since a disposal path cannot report a send failure to anyone.
fe4a9d4dServe_Connection waits for QUIC.Is_Connected against one deadline, the absolute
handshake timeout measured from the connection epoch. A peer that sends an
Initial and then stops answering is indistinguishable from one that is merely
slow, so its worker waits out the full budget and its Connection_Registry slot
stays taken. A cooperative peer now sends a close, but a crashed, partitioned,
or hostile one still does not.
Solution: Deadline an unconnected peer on no progress as well as elapsed time
Connection_State records the arrival of each inbound datagram, and the
pre-handshake wait takes the earlier of the absolute handshake deadline and
that arrival plus Handshake_Idle_Timeout. The absolute budget still bounds a
peer that keeps sending; the no-progress budget reclaims the slot from one that
stops.
Default_Handshake_Idle_Timeout is three seconds. Handshake retransmission on a
lossy path arrives well inside that window and counts as progress, so only a
silent peer reaches it. Serve_Listener and both Serve overloads take an
override. Routing.Serve is left alone: it gets the shorter reclamation from the
default without adding a parameter to eight overloads.
439a9fb6Both reclamation paths, the peer's transport CONNECTION_CLOSE and the server's
no-progress deadline, are only observable as the absence of a stall. No test
saturates a bounded listener with half-open handshakes, so either could regress
into the ten-second wait it replaced without a failure anywhere.
Solution: Saturate a listener of capacity N and require a fresh Initial
http3_handshake_abandon runs two phases against a capacity-three listener whose
handshake timeout is twenty seconds, far beyond every deadline it asserts, so a
slot reclaimed inside one of them cannot be explained by that timeout. Each
phase fills every slot with a handshake that will not complete, asserts that a
further probe is refused, releases the lanes, and then requires a fresh Initial
to be admitted.
The first phase abandons with closes and leaves the no-progress deadline out of
range; the second goes silent and relies on the deadline alone. Odd lanes
consume the ServerHello first so they abandon from the Handshake space, while
even lanes abandon from the Initial space, covering both protection levels. The
probe repeats its Initial while no worker answers, the way a real client's probe
timeout would, so a slot released partway through the budget is still taken up.
Each phase was confirmed to fail on its own: removing the driver's peer-close
reporting fails the first, and dropping the no-progress term from the deadline
fails the second.
439a9fb6 → updated to dac7fa90index 377df96f2 source commits
a3866c57Flyology.QUIC.Connections exposes loss recovery only for the application
packet-number space. The Initial and Handshake spaces track nothing they
have sent, discard the peer's ACK frames after parsing them, and never
acknowledge an ack-eliciting Initial or Handshake packet, which RFC 9000
13.2.1 requires immediately. Nothing arms the RFC 9002 6.2 probe timeout
those spaces need.
Both HTTP/3 drivers inherit the gap. Start_Connection loops on Receive_One
until the transport connects and Serve_Connection waits up to
Handshake_Timeout for progress; neither retransmits its own flight. A single
dropped client Initial or dropped server flight therefore costs the caller's
whole deadline rather than one probe timeout, which is how an ordinary
transient loss became a hard CI failure in the raw handshake lane of
tests/http3_server_integration.adb. That lane now works around the gap by
resending its own flight; the gap itself is still open.
Solution: Give the Initial and Handshake spaces RFC 9002 loss recovery
Each space now owns a sent-packet ledger and retains the CRYPTO ranges it
has sent in Crypto_Flight, so a probe can rebuild a range under a fresh
packet number rather than resending a packet QUIC forbids repeating. Peer
ACK frames drive Sent_Packet_Policy.Apply_ACK and yield the delivery, loss,
and RTT events the spaces previously threw away. Every packet a space builds
carries the acknowledgment it owes, and a datagram that produces no response
sends an ACK-only packet, so a peer's probe timeout clears within one round
trip.
Connection_Driver holds one path-wide Recovery_Policy state for both spaces:
a single probe count backing off together, and a probe timeout that excludes
max_ack_delay because handshake packets are acknowledged immediately.
Has_Recovery_Timeout, Recovery_Deadline, and Process_Timeout now report the
earlier of the handshake and application deadlines, and an expired handshake
timeout retransmits every unacknowledged CRYPTO range. A client whose peer
has not completed address validation sends the 6.2.2.1 anti-deadlock probe
instead. Progress that can only follow delivery of a flight retires it
without waiting for an ACK.
The HTTP/3 drivers needed almost nothing. Both already bounded each receive
by Recovery_Deadline and processed an expired timer, so widening the timer to
cover the handshake spaces made their loops retransmit; Start_Client gained a
defaulted Now so the timer has an anchor.
The raw QUIC lane of the server integration test carried a stand-in for this
gap: it retained the last flight it sent and resent it when a receive expired,
because nothing below the test retransmitted. The transport owns that now, so
the lane drives Process_Timeout against its own clock and the retained flight
is gone. Its listener sizing and the patience of its response loop are
unchanged, since neither depended on the missing timeout.
Handshake-space congestion is accounted but never gates a send: the flight
is far below the initial window and RFC 9002 permits probing regardless.
Immediate acknowledgment costs one extra packet per silent datagram,
including a padded client Initial ACK when only the ServerHello arrives;
that is what lets a peer stop retransmitting after one round trip.
Two new tests cover the loss. A deterministic transport test drives an
explicit clock through a dropped client Initial, a dropped server Handshake
flight, and the anti-deadlock probe, checking the exact first deadline, the
doubled backoff, and that an already-acknowledged Initial flight is not
retransmitted. An end-to-end test puts the routed server and the standard
client either side of a UDP relay that swallows one flight; with the probe
timer disabled it stalls for the full deadline and fails.
dac7fa90The sibling checkout pinned for local development also stood in for the QUIC
dependency's version, leaving it declared as "*". Local builds resolve through
the pin and never notice, but the pin cannot cross into the index, so anything
depending on this crate is free to resolve any QUIC release at all.
Solution: Declare the version the pin stands in for
Constrain the dependency to the development line the pinned checkout provides,
so the same version is selected once the pin is gone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
c124f3e3 on GitHub
0.1.0-devNotation3 parsing and serialization
flyology_rdf ~0.1.0-dev→ changed to >=0.1.0-dev & <0.2.00.1.0-devRDF 1.2 terms, streaming Turtle/TriG parsing, canonicalization
0.1.0-devSPARQL 1.1 query parsing and serialization
flyology_rdf ~0.1.0-dev→ changed to >=0.1.0-dev & <0.2.02730c85f on GitHub
0.1.0-devNotation3 parsing and serialization
flyology_rdf ~0.1.0-dev0.1.0-devRDF 1.2 terms, streaming Turtle/TriG parsing, canonicalization
flyology_iri >=0.1.1-dev & <0.2.00.1.0-devSPARQL 1.1 query parsing and serialization
flyology_rdf ~0.1.0-dev5f7622a7 on GitHub
0.1.0-devMemory-node topology reporting for Ada
gnat >=13 & <17b6c5fcbb on GitHub
0.1.0-devCaller-owned memory allocation algorithms for Ada
gnat >=13 & <178bece74f on GitHub
0.1.1-devLightweight tasks with ordinary Ada semantics for the GNAT runtime
gnat >=13 & <170.1.1-devAdaptive nanosecond-scale benchmarking for Ada
gnat >=13 & <170.1.1-devCache-line-aware storage and cache information for Ada
gnat >=13 & <170.1.1-devBounded in-memory tracing and gauges for Ada
gnat >=13 & <170.1.1-devHTTP client and server library for Flyology tasks
flyology ~0.1.1-devflyology_quic *0.1.1-devAllocation-conscious URI, IRI, and URL reference parser
0.1.1-devPostgres client and protocol server for Flyology tasks
flyology ~0.1.1-devhmac_ada ^0.2.0system_random ^1.0.00.1.1-devVersioned PostgreSQL SQL parser and owned Ada syntax trees
flyology_postgres_sql_v14 ~0.1.1-devflyology_postgres_sql_v15 ~0.1.1-devflyology_postgres_sql_v16 ~0.1.1-devflyology_postgres_sql_v17 ~0.1.1-devflyology_postgres_sql_v18 ~0.1.1-dev0.1.1-devShared native runtime for versioned Flyology PostgreSQL parsers
flyology_postgres ~0.1.1-dev0.1.1-devNative PostgreSQL 14 parser and owned Ada AST
flyology_postgres_sql_core ~0.1.1-dev0.1.1-devNative PostgreSQL 15 parser and owned Ada AST
flyology_postgres_sql_core ~0.1.1-dev0.1.1-devNative PostgreSQL 16 parser and owned Ada AST
flyology_postgres_sql_core ~0.1.1-dev0.1.1-devNative PostgreSQL 17 parser and owned Ada AST
flyology_postgres_sql_core ~0.1.1-dev0.1.1-devNative PostgreSQL 18 parser and owned Ada AST
flyology_postgres_sql_core ~0.1.1-dev0.1.1-devAda-native QUIC transport for Flyology tasks
flyology ~0.1.1-dev0.1.1-devPortable, strongly typed SIMD foundations for Ada
gnat 16.1.00.1.1-devA typed, declarative terminal user-interface toolkit for Ada
gnat >=13 & <17981209ad on GitHub
0.1.0Versioned PostgreSQL raw SQL parser and Ada syntax tree
flyology_postgres_sql_v14 ~0.1.0flyology_postgres_sql_v15 ~0.1.0flyology_postgres_sql_v16 ~0.1.0flyology_postgres_sql_v17 ~0.1.0flyology_postgres_sql_v18 ~0.1.0d4fb5506 on GitHub
0.1.0Native PostgreSQL 14 parser and owned Ada AST
flyology_postgres_sql_core ~0.1.00.1.0Native PostgreSQL 15 parser and owned Ada AST
flyology_postgres_sql_core ~0.1.00.1.0Native PostgreSQL 16 parser and owned Ada AST
flyology_postgres_sql_core ~0.1.00.1.0Native PostgreSQL 17 parser and owned Ada AST
flyology_postgres_sql_core ~0.1.00.1.0Native PostgreSQL 18 parser and owned Ada AST
flyology_postgres_sql_core ~0.1.0eb2fea94 on GitHub
0.1.0HTTP client and server library for Flyology tasks
flyology ~0.1.0flyology_quic ~0.1.02820c41c on GitHub
0.1.0Shared native runtime for versioned Flyology PostgreSQL parsers
flyology_postgres ~0.1.0cd3679c0 on GitHub
0.1.0Ada-native QUIC transport for Flyology tasks
flyology ~0.1.06d1ac914 on GitHub
0.1.0Postgres client and protocol server for Flyology tasks
flyology ~0.1.0hmac_ada ^0.2.0system_random ^1.0.053def6c7 on GitHub
0.1.0Experimental GNAT runtime extension for native and lightweight Ada tasks
gnat >=13 & <170.1.0Adaptive nanosecond-scale benchmarking for Ada
gnat >=13 & <170.1.0Cache-line-aware storage and cache information for Ada
gnat >=13 & <170.1.0Bounded in-memory tracing and gauges for Ada
gnat >=13 & <170.1.0Allocation-conscious URI, IRI, and URL reference parser
0.1.0Portable, strongly typed SIMD foundations for Ada
0.1.0A typed, declarative terminal user-interface toolkit for Ada
gnat >=13 & <1737eb0e5e on GitHub
16.2.0-patchset.1.1.0GNAT native compiler with a validated gnat-patches aggregate
fd3f3c65 on GitHub
13.2.0-patchset.1.1.0GNAT native compiler with a validated gnat-patches aggregate
14.2.0-patchset.1.1.0GNAT native compiler with a validated gnat-patches aggregate
15.3.0-patchset.1.1.0GNAT native compiler with a validated gnat-patches aggregate
16.1.0-patchset.1.1.0GNAT native compiler with a validated gnat-patches aggregate
b2a6ba64 on GitHub
GNAT native compiler with a validated gnat-patches aggregate
GNAT native compiler with a validated gnat-patches aggregate
GNAT native compiler with a validated gnat-patches aggregate