diff --git a/.github/workflows/fleet-converge.yml b/.github/workflows/fleet-converge.yml index 7900ff50b53..99da90b71d3 100644 --- a/.github/workflows/fleet-converge.yml +++ b/.github/workflows/fleet-converge.yml @@ -11,9 +11,9 @@ on: options: [srv1, srv2, srv3, srv4] type: choice mode: - description: "plan shows membership hunks; apply executes a prior plan artifact; org_actions_observe validates the org credential and refuses on runner-group drift; org_runner_roster_observe reads the organization's self-hosted runner registrations to exhaustion under the same token and refuses unless the roster is complete; app_control_plane_observe reads the gunbai-ci App registration and webhook config with a short-lived App JWT and refuses on hook-config drift; app_key_version_verify reads the gunbai-ci App private key at the EXACT Secret Manager version named by app_key_version, mints an installation token with it, and refuses unless GitHub accepts it and the key's rotation deadline has not passed -- no add, disable or destroy; runner_browser_toolchain_converge installs the declared Playwright/Chromium toolchain (apt host libraries as the administrator, digest-pinned node, Playwright and Chromium archives into the job user's root) on the selected host, which must be in the pool that runs the floor job, and refuses unless every digest, version and host library reads back and headless Chromium renders a local page; microvm_host_converge installs the cited Firecracker release on the selected host and reads the kvm grant back, refusing by name when the grant has not landed (the grant itself is applied by the full-host apply spine); microvm_network_apply stages the slot and host network files the model renders at the named expected_revision as root:root 0600 in a root-only directory over the fleet SSH edge as the host's ADMINISTRATOR, installs them with the modeled operations, reloads networkd, systemd-sysctl and the nft loader unit, and reads the ruleset back -- the job user is granted none of it, because install plus systemctl over content that principal can write is arbitrary root for any pull request; guest_image_observe reports the micro-VM base artifacts and their measured digests on the selected host and builds nothing; guest_image_converge builds the runner guest image there; microvm_boot_probe boots that image and reads its serial console; spark_grants and spark_bootstrap prepare the selected Spark; spark_serving_apply applies the promoted fabric groups' pair vLLM units over the password session, workers before heads; spark_native_serving_apply relaunches group B's native four-rank arm as ONE BOUNDED TRANSACTION -- an all-host preflight that mutates nothing and refuses on any unresolved artifact from an earlier run, the incumbent UNIT preserved (plus a diagnostic inspect of the incumbent container -- the container itself cannot outlive its unit, because these run --rm in the foreground under systemd), the head applied BEFORE the workers so every rank joins the head's new rendezvous store, then a readback of the COMPLETE realization through each container's own inspect -- every field the create spec states, the effective --node-rank off that container's argv, and NCCL's typed transport verdict on that incarnation -- plus the head's front door answering with this arm's served model, and then commit or a rollback of the whole arm -- and it is a separate mode because it reloads a 328 GB arm and nobody should reach it by asking for the pair units; spark_runtime_image_probe pulls the pinned runtime image on the selected Spark and reads its capabilities from inside its own digest, changing nothing; spark_wireless_link_converge reads the selected Spark's Wi-Fi power save (runtime via iw, persisted via the NetworkManager profile), link and kernel disconnect count, sets whichever realization differs from the declared intent, and refuses unless the readback decides Noop; spark_grants_observe reads every procured Spark's sudo grant listing as gunbc-automation and the bootstrap principal, and its sudoers drop-in shape, and writes nothing; spark_v41_checkpoint_materialize fetches the admitted published DeepSeek V4.1 files onto the selected Group A Spark (about 510 GB; spark_v41_row_store_encode encodes the eight Engram row stores from the verified shards on the selected Group A Spark and reads each store's sha256; spark_v41_row_store_readback reads those stores back at their header, first and last record and every rank seam, with the published source rows at the same rows, and writes nothing; spark_v41_engram_differential compares upstream's Engram lookup kernel with the design-B file-backed lookup over sampled real rows of every row store, byte for byte, and writes nothing; it states the requirement and refuses before fetching when the disk cannot hold it), publishes each only after its sha256 matches the manifest, leaves a present file with the right digest alone and refuses one with the wrong digest, and reads the storage-backed Engram spans from the verified shards; a transfer runs detached and a rerun reattaches; spark_v41_runtime_image_build PRODUCES the DeepSeek V4.1 image on the selected Spark -- it verifies the candidate's three FlashInfer wheels against the digests the candidate keys, converges the patched source tree, builds from it, reads the produced configuration digest back from inside the image through the probe route, and admits that digest against the candidate's own recipe, refusing a digest that does not recompute from it -- and it is a separate mode from the probe because it occupies one host for hours where the probe occupies it for minutes; spark_v41_runtime_image_distribute moves that produced image, named by the configuration digest its production receipt read back, from the host that receipt names to the selected Group A Spark -- save into its fabric blob root, pulled by the target straight over the fabric rail, load -- after stating its size against every filesystem a copy lands on, leaves a target already holding the digest untouched, refuses a target holding a different image under the tag, and refuses unless the target's image inspect Id reads back as that digest; spark_v41_group_a_launch reads the production image back under its tag on every Group A host, reads its registry inside its digest and every host's occupancy, and only when Group A is suspended for this candidate with every host held and vacant stages the Engram manifest and applies the V4.1 four-rank arm as one transaction at the capacity measurement's shape -- the target names only the session host; spark_v41_serving_load runs the shared serving-load runner against the V4.1 head (target must be srv6): one vllm bench serve step per capacity-measurement concurrency, metrics scraped around each, host pressure read on every Group A rank, and the staircase stop rules applied over the worst rank; it changes no unit and writes only its receipt; host_reset_return drives a rostered reset subject through its controller and measures the return FROM A PEER, and takes reset_observer rather than host because the observer must not be the subject; runner_host_file_observe reads the runner teardown drop-in, the needrestart deferral and the loaded teardown on the selected host as its administrator and writes nothing; runner_host_file_converge writes whichever of those two files differs, reloads systemd only if the drop-in changed, and refuses unless the manager then reports the declared teardown; microvm_controller_install writes the microVM slot controller's root-owned release locus (gunbc + sources + Firecracker + jailer) and the gunbc-microvm-slot@ template unit and the gunbc-microvm-slot-reserve broker unit on srv1 and starts neither; microvm_slot_reserve starts that broker unit once on srv1, which reserves the shakedown cell through the fabric broker route from inside the root process (slot, demand and offer derived from the model, never inputs), and uploads that invocation's reservation receipt, failing unless the reservation committed; microvm_slot_start starts the shakedown slot's controller unit once on srv1 (the instance is derived from the model, never an input), waits for it bounded by the unit's own stop timeout, and uploads that invocation's controller receipt; microvm_runner_group_ensure (srv1 only) reads the organization's runner groups and, only when the microvm-shakedown group is absent, files ONE operator approval, creates it restricted to the shakedown workflow on the default branch, and refuses unless the readback holds that restriction; mtcollins1_census_qemu_host_observe reads the selected host's KVM device and, under the job user's census QEMU root, the qemu-system-aarch64 build, its ldd libraries and the AAVMF images against their pins, and writes nothing; mtcollins1_census_qemu_toolchain_converge places that root as the job user (digest-pinned noble .debs unpacked with dpkg-deb -x, no apt, no Recommends) and refuses unless the same observe reads it ready; workspace_source_pack (host=srv1) enumerates the operator workspace as the job user, drops every path the credential exclusion row names and every build output, tars exactly the remainder, reads the archive back and refuses if any member is excluded, and puts it into the workspace bucket under its SHA-256, refusing by name when the runner cannot read a source root; workspace_checkpoint_measure (host=srv3, workspace_source_object = that SHA-256) refuses unless the job user's home is on btrfs, then times a read-only subvolume snapshot, a pinned kopia scan of it, the content-addressed chunk upload and the gated revision commit, and receipts every time with the 10 s / 60 s / 10 GB draft verdicts; workspace_checkpoint_restore (host=srv3) commits a small authored workspace, puts one chunk with no head advance, destroys the directory, restores it from the head closure and requires byte-equality with the uncommitted chunk absent -- both srv3 modes refuse their commit while the R2 head's CAS ground is uncited" + description: "plan shows membership hunks; apply executes a prior plan artifact; org_actions_observe validates the org credential and refuses on runner-group drift; org_runner_roster_observe reads the organization's self-hosted runner registrations to exhaustion under the same token and refuses unless the roster is complete; app_control_plane_observe reads the gunbai-ci App registration and webhook config with a short-lived App JWT and refuses on hook-config drift; app_key_version_verify reads the gunbai-ci App private key at the EXACT Secret Manager version named by app_key_version, mints an installation token with it, and refuses unless GitHub accepts it and the key's rotation deadline has not passed -- no add, disable or destroy; runner_browser_toolchain_converge installs the declared Playwright/Chromium toolchain (apt host libraries as the administrator, digest-pinned node, Playwright and Chromium archives into the job user's root) on the selected host, which must be in the pool that runs the floor job, and refuses unless every digest, version and host library reads back and headless Chromium renders a local page; microvm_host_converge installs the cited Firecracker release on the selected host and reads the kvm grant back, refusing by name when the grant has not landed (the grant itself is applied by the full-host apply spine); microvm_network_apply stages the slot and host network files the model renders at the named expected_revision as root:root 0600 in a root-only directory over the fleet SSH edge as the host's ADMINISTRATOR, installs them with the modeled operations, reloads networkd, systemd-sysctl and the nft loader unit, and reads the ruleset back -- the job user is granted none of it, because install plus systemctl over content that principal can write is arbitrary root for any pull request; guest_image_observe reports the micro-VM base artifacts and their measured digests on the selected host and builds nothing; guest_image_converge builds the runner guest image there; microvm_boot_probe boots that image and reads its serial console; spark_grants and spark_bootstrap prepare the selected Spark; spark_serving_apply applies the promoted fabric groups' pair vLLM units over the password session, workers before heads; spark_native_serving_apply relaunches group B's native four-rank arm as ONE BOUNDED TRANSACTION -- an all-host preflight that mutates nothing and refuses on any unresolved artifact from an earlier run, the incumbent UNIT preserved (plus a diagnostic inspect of the incumbent container -- the container itself cannot outlive its unit, because these run --rm in the foreground under systemd), the head applied BEFORE the workers so every rank joins the head's new rendezvous store, then a readback of the COMPLETE realization through each container's own inspect -- every field the create spec states, the effective --node-rank off that container's argv, and NCCL's typed transport verdict on that incarnation -- plus the head's front door answering with this arm's served model, and then commit or a rollback of the whole arm -- and it is a separate mode because it reloads a 328 GB arm and nobody should reach it by asking for the pair units; spark_runtime_image_probe pulls the pinned runtime image on the selected Spark and reads its capabilities from inside its own digest, changing nothing; spark_wireless_link_converge reads the selected Spark's Wi-Fi power save (runtime via iw, persisted via the NetworkManager profile), link and kernel disconnect count, sets whichever realization differs from the declared intent, and refuses unless the readback decides Noop; spark_grants_observe reads every procured Spark's sudo grant listing as gunbc-automation and the bootstrap principal, and its sudoers drop-in shape, and writes nothing; spark_v41_checkpoint_materialize fetches the admitted published DeepSeek V4.1 files onto the selected Group A Spark (about 510 GB; spark_v41_row_store_encode encodes the eight Engram row stores from the verified shards on the selected Group A Spark and reads each store's sha256; spark_v41_row_store_readback reads those stores back at their header, first and last record and every rank seam, with the published source rows at the same rows, and writes nothing; spark_v41_engram_differential compares upstream's Engram lookup kernel with the design-B file-backed lookup over sampled real rows of every row store, byte for byte, and writes nothing; it states the requirement and refuses before fetching when the disk cannot hold it), publishes each only after its sha256 matches the manifest, leaves a present file with the right digest alone and refuses one with the wrong digest, and reads the storage-backed Engram spans from the verified shards; a transfer runs detached and a rerun reattaches; spark_v41_runtime_image_build PRODUCES the DeepSeek V4.1 image on the selected Spark -- it verifies the candidate's three FlashInfer wheels against the digests the candidate keys, converges the patched source tree, builds from it, reads the produced configuration digest back from inside the image through the probe route, and admits that digest against the candidate's own recipe, refusing a digest that does not recompute from it -- and it is a separate mode from the probe because it occupies one host for hours where the probe occupies it for minutes; spark_v41_runtime_image_distribute moves that produced image, named by the configuration digest its production receipt read back, from the host that receipt names to the selected Group A Spark -- save into its fabric blob root, pulled by the target straight over the fabric rail, load -- after stating its size against every filesystem a copy lands on, leaves a target already holding the digest untouched, refuses a target holding a different image under the tag, and refuses unless the target's image inspect Id reads back as that digest; spark_v41_group_a_launch reads the production image back under its tag on every Group A host, reads its registry inside its digest and every host's occupancy, and only when Group A is suspended for this candidate with every host held and vacant stages the Engram manifest and applies the V4.1 four-rank arm as one transaction at the capacity measurement's shape -- the target names only the session host; spark_v41_serving_load runs the shared serving-load runner against the V4.1 head (target must be srv6): one vllm bench serve step per capacity-measurement concurrency, metrics scraped around each, host pressure read on every Group A rank, and the staircase stop rules applied over the worst rank; it changes no unit and writes only its receipt; host_reset_return drives a rostered reset subject through its controller and measures the return FROM A PEER, and takes reset_observer rather than host because the observer must not be the subject; runner_host_file_observe reads the runner teardown drop-in, the needrestart deferral and the loaded teardown on the selected host as its administrator and writes nothing; runner_host_file_converge writes whichever of those two files differs, reloads systemd only if the drop-in changed, and refuses unless the manager then reports the declared teardown; microvm_controller_install writes the microVM slot controller's root-owned release locus (gunbc + sources + Firecracker + jailer) and the gunbc-microvm-slot@ template unit and the gunbc-microvm-slot-reserve broker unit on srv1 and starts neither; microvm_slot_reserve starts that broker unit once on srv1, which reserves the shakedown cell through the fabric broker route from inside the root process (slot, demand and offer derived from the model, never inputs), and uploads that invocation's reservation receipt, failing unless the reservation committed; microvm_slot_start starts the shakedown slot's controller unit once on srv1 (the instance is derived from the model, never an input), waits for it bounded by the unit's own stop timeout, and uploads that invocation's controller receipt; microvm_runner_group_ensure (srv1 only) reads the organization's runner groups and, only when the microvm-shakedown group is absent, files ONE operator approval, creates it restricted to the shakedown workflow on the default branch, and refuses unless the readback holds that restriction; mtcollins1_census_qemu_host_observe reads the selected host's KVM device and, under the job user's census QEMU root, the qemu-system-aarch64 build, its ldd libraries and the AAVMF images against their pins, and writes nothing; mtcollins1_census_qemu_toolchain_converge places that root as the job user (digest-pinned noble .debs unpacked with dpkg-deb -x, no apt, no Recommends) and refuses unless the same observe reads it ready; workspace_source_pack (host=srv1, agent_state_slot = an existing .claude@, no default) enumerates the operator workspace as the job user, drops every path the credential exclusion row names and every build output, stands same-size seeded placeholders in for transcript files, tars the remainder and the placeholders, reads the archive back and refuses if any credential is carried or any placeholder's archived bytes are not its staged digest, and puts it into the workspace bucket under its SHA-256, refusing by name when the runner cannot read a source root; workspace_checkpoint_measure (host=srv3, workspace_source_object = that SHA-256) refuses unless the job user's home is on btrfs, then times a read-only subvolume snapshot, a pinned kopia scan of it, the content-addressed chunk upload and the gated revision commit, and receipts every time, withholding the 10 s / 60 s draft verdicts while churn is unmeasured and deciding the 10 GB quota over the static size; workspace_checkpoint_restore (host=srv3) commits a small authored workspace, puts one chunk with no head advance, destroys the directory, restores it from the head closure and requires byte-equality with the uncommitted chunk absent -- both srv3 modes refuse their commit while the R2 head's CAS ground is uncited; workspace_transcript_rate_observe (host=srv1, agent_state_slot as for workspace_source_pack, admitted by the same rule) samples the size and mtime of every transcript file the pack's transcript exclusion rows match under that slot and the Codex home, once per interval over a ten-minute window, never opening content, and receipts per-interval growth, creations, truncations and active writers with mean and p95 bytes/s as ONE sample of workload" required: true - options: [plan, launch_environment_plan, allocation_store_plan, workspace_commissioning_plan, apply, org_actions_observe, app_control_plane_observe, microvm_host_converge, microvm_network_observe, microvm_network_apply, guest_image_observe, guest_image_converge, microvm_boot_probe, spark_grants, spark_bootstrap, spark_serving_apply, spark_native_serving_apply, spark_runtime_image_probe, spark_wireless_link_converge, spark_grants_observe, spark_v41_checkpoint_materialize, spark_v41_row_store_encode, spark_v41_row_store_readback, spark_v41_engram_differential, spark_runtime_image_produce, spark_runtime_image_distribute, spark_arm_group_load, spark_arm_checkpoint_materialize, spark_arm_group_observe, spark_arm_group_launch_plan, spark_arm_group_launch, spark_v41_serving_load, dashboard_deploy, approval_broker_dark_install, microvm_controller_install, microvm_slot_reserve, microvm_slot_start, rlm_launch_deployment_receipt, host_reset_return, runner_host_file_observe, runner_host_file_converge, site_pxe_edge_observe, site_pxe_edge_converge, runner_password_session_tool_converge, runner_browser_toolchain_converge, r2_mint_preflight, r2_object_write_mint, org_runner_roster_observe, approval_keyring_converge, approval_device_enrolment_code_issue, mtcollins1_boot, mtcollins1_fan_observe, mtcollins1_ui_bundle_observe, mtcollins1_kvm_observer_observe, mtcollins1_census_image_publish, mtcollins1_census_member_readback, mtcollins1_census_qemu_host_observe, mtcollins1_census_qemu_toolchain_converge, host_credential_custody_converge, app_key_version_verify, r2_bucket_ensure, r2_bucket_admin_mint, r2_workspace_object_read_mint, r2_workspace_object_write_mint, r2_cache_object_read_mint, r2_cache_object_write_mint, r2_conditional_put_race_probe, workspace_source_pack, workspace_checkpoint_measure, workspace_checkpoint_restore, fabric_writer_identity_observe, pair_serving_d0, microvm_runner_group_ensure, gcp_iam_converge, namecheap_observe, printer] + options: [plan, launch_environment_plan, allocation_store_plan, workspace_commissioning_plan, apply, org_actions_observe, app_control_plane_observe, microvm_host_converge, microvm_network_observe, microvm_network_apply, guest_image_observe, guest_image_converge, microvm_boot_probe, spark_grants, spark_bootstrap, spark_serving_apply, spark_native_serving_apply, spark_runtime_image_probe, spark_wireless_link_converge, spark_grants_observe, spark_v41_checkpoint_materialize, spark_v41_row_store_encode, spark_v41_row_store_readback, spark_v41_engram_differential, spark_runtime_image_produce, spark_runtime_image_distribute, spark_arm_group_load, spark_arm_checkpoint_materialize, spark_arm_group_observe, spark_arm_group_launch_plan, spark_arm_group_launch, spark_v41_serving_load, dashboard_deploy, approval_broker_dark_install, microvm_controller_install, microvm_slot_reserve, microvm_slot_start, rlm_launch_deployment_receipt, host_reset_return, runner_host_file_observe, runner_host_file_converge, site_pxe_edge_observe, site_pxe_edge_converge, runner_password_session_tool_converge, runner_browser_toolchain_converge, r2_mint_preflight, r2_object_write_mint, org_runner_roster_observe, approval_keyring_converge, approval_device_enrolment_code_issue, mtcollins1_boot, mtcollins1_fan_observe, mtcollins1_ui_bundle_observe, mtcollins1_kvm_observer_observe, mtcollins1_census_image_publish, mtcollins1_census_member_readback, mtcollins1_census_qemu_host_observe, mtcollins1_census_qemu_toolchain_converge, host_credential_custody_converge, app_key_version_verify, r2_bucket_ensure, r2_bucket_admin_mint, r2_workspace_object_read_mint, r2_workspace_object_write_mint, r2_cache_object_read_mint, r2_cache_object_write_mint, r2_conditional_put_race_probe, workspace_source_pack, workspace_checkpoint_measure, workspace_checkpoint_restore, workspace_transcript_rate_observe, fabric_writer_identity_observe, pair_serving_d0, microvm_runner_group_ensure, gcp_iam_converge, namecheap_observe, printer] type: choice target: description: "Spark target host for the spark_* modes, naming the administrator credential the run materializes (any rostered Spark: srv5, srv6, srv7, srv8, srv9, srv10, srv11, srv12); apply reads the subject off the plan artifact" @@ -118,6 +118,10 @@ on: required: false options: ["false", "true"] type: choice + agent_state_slot: + description: "workspace_source_pack and workspace_transcript_rate_observe: the Claude auth slot whose agent state is packed, as in /home/briansrls/.claude@ on srv1; admitted only if that directory exists, refused naming the observed slots otherwise; no default" + required: false + type: string workspace_source_object: description: "workspace_checkpoint_measure only: the lower-hex SHA-256 the workspace_source_pack receipt printed as address= (the archive's own digest; the fetched bytes must hash to it)" required: false @@ -187,7 +191,7 @@ jobs: - name: Derive release-bins key (pre-materialization axes; sha256 at cross-run scope) id: release_bins_key run: | - if [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ]; then + if [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_transcript_rate_observe' ]; then set -e ROOT=$('git' 'rev-parse' '--show-toplevel') DIRTY=$('git' '-C' "$ROOT" 'status' '--porcelain' '--untracked-files=no') @@ -393,7 +397,7 @@ jobs: - name: Decide release-bins outcome; verify a stored pack completely before any restored binary runs id: release_bins_outcome run: | - if [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ]; then + if [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_transcript_rate_observe' ]; then set -e ROOT=$('git' 'rev-parse' '--show-toplevel') STAGE="$ROOT"'/.release-bins-staging' @@ -531,7 +535,7 @@ jobs: - name: Build release bins (the dispatched mode's product) id: release_build run: | - if [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ]; then + if [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_transcript_rate_observe' ]; then cargo build --release -p v1-compiler --bin claim_executor --bin gunbc else ROOT=$('git' 'rev-parse' '--show-toplevel') @@ -566,7 +570,7 @@ jobs: timeout-minutes: 5 - name: Pack release bins with member manifest into staging (build outcomes only; tar keeps exec bits) run: | - if [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ]; then + if [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_transcript_rate_observe' ]; then set -e ROOT=$('git' 'rev-parse' '--show-toplevel') STAGE="$ROOT"'/.release-bins-staging' @@ -639,7 +643,7 @@ jobs: - name: Verify the handed-off release bins against the build key, then promote and run --verify-build-artifacts (fail-closed) id: release_bins run: | - if [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ]; then + if [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_transcript_rate_observe' ]; then set -e ROOT=$('git' 'rev-parse' '--show-toplevel') '[' '-n' "$RELEASE_BINS_KEY" ']' || ('echo' '::error title=release-bins ConsumerKeyMissing::the build job published no release-bins key'; exit 1) @@ -1514,6 +1518,7 @@ jobs: cat "$ROOT/target/workspace-source-pack-receipt.txt" env: WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} + WORKSPACE_AGENT_STATE_SLOT: ${{ github.event.inputs.agent_state_slot }} if: github.event.inputs.mode == 'workspace_source_pack' timeout-minutes: 120 - name: Upload workspace source pack receipt @@ -1570,6 +1575,27 @@ jobs: compression-level: 0 if: always() && github.event.inputs.mode == 'workspace_checkpoint_restore' timeout-minutes: 10 + - name: "Workspace transcript append rate: size and mtime of every transcript file on srv1, sampled per interval over a declared window, content never read" + id: workspace_transcript_rate_observe + run: |- + ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/fabric/workspace_transcript_rate.dag --function workspace_transcript_rate_observe_wet + cat "$ROOT/target/workspace-transcript-rate-receipt.txt" + env: + WORKSPACE_AGENT_STATE_SLOT: ${{ github.event.inputs.agent_state_slot }} + if: github.event.inputs.mode == 'workspace_transcript_rate_observe' + timeout-minutes: 30 + - name: Upload workspace transcript append-rate receipt + id: workspace_transcript_rate_observe_receipt_upload + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f + with: + name: workspace-transcript-rate-observe + path: target/workspace-transcript-rate-receipt.txt + if-no-files-found: error + retention-days: 90 + compression-level: 0 + if: always() && github.event.inputs.mode == 'workspace_transcript_rate_observe' + timeout-minutes: 10 - name: Materialize approval MAC keys on srv1 (SM pinned -> sha256 prefix -> /etc/gunbc-roadmap 0640) id: approval_keyring_converge run: |- diff --git a/.github/workflows/witnesses.yml b/.github/workflows/witnesses.yml index 4455164783a..60020c08bd5 100644 --- a/.github/workflows/witnesses.yml +++ b/.github/workflows/witnesses.yml @@ -858,6 +858,31 @@ jobs: if (! '[' '-f' "$GUNBC_FLOOR_RECEIPT" ']') || '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' || ('[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT"))) || ('[' "$GUNBC_FLOOR_CLASS" '=' 'none' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT")) && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=infra' "$GUNBC_FLOOR_RECEIPT"))); then 'printf' 'class=%s\nsignature=%s\nexit=%s\n' "$GUNBC_FLOOR_CLASS" "$GUNBC_FLOOR_SIGNATURE" "$GUNBC_FLOOR_EXIT" > "$GUNBC_FLOOR_RECEIPT"; if '[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']'; then 'echo' '::error title=environment::floor_class='"$GUNBC_FLOOR_CLASS"' signature='"$GUNBC_FLOOR_SIGNATURE"' exit='"$GUNBC_FLOOR_EXIT"'; this is not a verdict about the diff. Attempt receipt: '"$GUNBC_FLOOR_RECEIPT"; fi; if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']'; then 'echo' '::error title=subject::floor_class='"$GUNBC_FLOOR_CLASS"' exit='"$GUNBC_FLOOR_EXIT"'; read the step log for the subject defect'; fi; fi 'exit' "$GUNBC_FLOOR_EXIT" + if: "!cancelled()" + - name: Run the slice cost controls + id: slice_cost_controls + run: |+ + GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' + 'set' '+e' + 'set' '-o' 'pipefail' + ('sh' '-e' '-c' 'cargo test --release -p v1-stage0-runtime --lib -- list_slice_tests') 2>&1 | 'tee' "$GUNBC_FLOOR_LOG" + GUNBC_FLOOR_EXIT="$?" + GUNBC_FLOOR_RECEIPT='gunbc-floor-outcome.txt' + GUNBC_FLOOR_CLASS='structural' + GUNBC_FLOOR_SIGNATURE='' + if '[' "$GUNBC_FLOOR_EXIT" '-eq' '126' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandInvokedCannotExecute'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '127' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandNotFound'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '0' ']'; then GUNBC_FLOOR_CLASS='none'; GUNBC_FLOOR_SIGNATURE=''; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'MemoryStallRefusedPageThrash' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='MemoryStallRefusedPageThrash'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'The runner has received a shutdown signal' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='RunnerLost'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: error: failed to execute compile' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFailedToExecuteCompile'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' '(exit status: 254)' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheRustcWrapperExit254'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: encountered fatal error' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFatalError'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'failed to spawn' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ProcessSpawnFailure'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'Resource temporarily unavailable' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ResourceTemporarilyUnavailable'; fi + if (! '[' '-f' "$GUNBC_FLOOR_RECEIPT" ']') || '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' || ('[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT"))) || ('[' "$GUNBC_FLOOR_CLASS" '=' 'none' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT")) && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=infra' "$GUNBC_FLOOR_RECEIPT"))); then 'printf' 'class=%s\nsignature=%s\nexit=%s\n' "$GUNBC_FLOOR_CLASS" "$GUNBC_FLOOR_SIGNATURE" "$GUNBC_FLOOR_EXIT" > "$GUNBC_FLOOR_RECEIPT"; if '[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']'; then 'echo' '::error title=environment::floor_class='"$GUNBC_FLOOR_CLASS"' signature='"$GUNBC_FLOOR_SIGNATURE"' exit='"$GUNBC_FLOOR_EXIT"'; this is not a verdict about the diff. Attempt receipt: '"$GUNBC_FLOOR_RECEIPT"; fi; if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']'; then 'echo' '::error title=subject::floor_class='"$GUNBC_FLOOR_CLASS"' exit='"$GUNBC_FLOOR_EXIT"'; read the step log for the subject defect'; fi; fi + 'exit' "$GUNBC_FLOOR_EXIT" + if: "!cancelled()" - name: "Stage0 mirrors match one emission by this seed: required CI build lane" id: stage0_regen diff --git a/dag/extdeps/crypto/seeded_filler.dag b/dag/extdeps/crypto/seeded_filler.dag new file mode 100644 index 00000000000..d69439ba684 --- /dev/null +++ b/dag/extdeps/crypto/seeded_filler.dag @@ -0,0 +1,94 @@ +module extdeps.crypto.seeded_filler + +import std.types { String, NonEmptyStr, Int, FilePath } +import std.measure { ByteSize, byte_size, measure_count } +import std.optional { Present, Absent } +import std.decl_ref { DeclarationRef, WholeDeclaration } +import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } +import extdeps.uri { Uri, Https } +import extdeps.shell +import extdeps.tools.coreutils_truncate +import extdeps.tools.coreutils_stat +import extdeps.tools.openssl + +// A DETERMINISTIC, NON-SECRET, INCOMPRESSIBLE FILE OF EXACTLY N BYTES, KEYED BY A SEED. +// +// The construction is the GNU coreutils manual's own seeded random source ("Sources of random data"): +// the AES-256-CTR keystream that `openssl enc -aes-256-ctr -pass pass: -nosalt` produces over +// zeros. Here the zeros are a file `truncate -s N` sized exactly, so the keystream is exactly N bytes +// long without a pipe: CTR is a stream mode, so output length equals input length. It is +// reproducible (one seed, one key, one stream), two seeds give unrelated streams, and an AES +// keystream is indistinguishable from random, so neither gzip nor a chunk store's dedup finds +// structure in it -- which is why it, and not zeros or a repeated pattern, stands in for content +// whose SIZE must be carried while its bytes must not be: a compressible filler would understate +// what storing the real content costs. The seed is in argv and is NOT a secret; the filler carries +// no information beyond its seed and its length. +data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { + uri: Uri { scheme: Https, locator: "www.gnu.org/software/coreutils/manual/html_node/Random-sources.html" } +} + +data seeded_filler_openssl_enc_authority: ExternalAuthority = ExternalAuthority { + uri: Uri { scheme: Https, locator: "docs.openssl.org/3.0/man1/openssl-enc/" } +} + +data extdeps_model_scope: ExternalModelScope = ExternalModelScope { + subject: ExternalSubjectRef { + declaration: DeclarationRef { module_path: "extdeps.crypto.seeded_filler", decl_name: "seeded_filler_write", field: WholeDeclaration } + }, + first_citation: extdeps.crypto.seeded_filler.extdeps_external_authority_anchor, + further_citations: [extdeps.crypto.seeded_filler.seeded_filler_openssl_enc_authority] +} + +// WHICH STEP REFUSED. The size readback is a step: a filler whose stat size is not the requested +// size is refused, never returned. +type SeededFillerStep + = FillerZeroSized + | FillerKeystreamWritten + | FillerZeroRemoved + | FillerSizeReadBack + +type SeededFiller + = SeededFillerWritten { path: NonEmptyStr, size: ByteSize } + | SeededFillerRefused { path: NonEmptyStr, step: SeededFillerStep, detail: String } + +fn seeded_filler_step_words(step: SeededFillerStep) -> String { + match step { + FillerZeroSized => "truncate" + FillerKeystreamWritten => "openssl-enc" + FillerZeroRemoved => "zero-input-removal" + FillerSizeReadBack => "size-readback" + } +} + +// WRITE THE FILLER AT `path`, whose parent directory must exist. The zero input is a sibling +// `.zero`, sparse on every filesystem truncate extends, and removed before returning. +fn seeded_filler_write(seed: NonEmptyStr, size: ByteSize, path: NonEmptyStr) -> SeededFiller { + let zero = concat(path as String, ".zero") as NonEmptyStr + let sized = coreutils.Truncate.ToSize(size_bytes: to_string(measure_count(m: size)) as NonEmptyStr, path: zero) + if sized.success == false { + SeededFillerRefused { path: path, step: FillerZeroSized, detail: "truncate could not size the zero input" } + } else { + let enc = openssl.Enc.Aes256CtrPassphrase(passphrase: seed, input_path: zero, output_path: path) + let removed = shell.Remove.FileForce(path: zero as FilePath) + if enc.success == false { + SeededFillerRefused { path: path, step: FillerKeystreamWritten, detail: enc.stderr } + } else if removed.success == false { + SeededFillerRefused { path: path, step: FillerZeroRemoved, detail: concat("the zero input could not be removed: ", zero as String) } + } else { + seeded_filler_readback(path: path, size: size) + } + } +} + +fn seeded_filler_readback(path: NonEmptyStr, size: ByteSize) -> SeededFiller { + let st = coreutils.Stat.PathSize(path: path) + match parse_int(s: replace(st.size_text, "\n", "")) { + Absent => SeededFillerRefused { path: path, step: FillerSizeReadBack, detail: concat("stat printed no size: ", st.size_text) } + Present { value: n } => + if st.success && n == measure_count(m: size) { + SeededFillerWritten { path: path, size: size } + } else { + SeededFillerRefused { path: path, step: FillerSizeReadBack, detail: join(["the filler is ", to_string(n), " bytes, not ", to_string(measure_count(m: size))], "") } + } + } +} diff --git a/dag/extdeps/exec/command.dag b/dag/extdeps/exec/command.dag index 4041e38d681..3c7e095cf6b 100644 --- a/dag/extdeps/exec/command.dag +++ b/dag/extdeps/exec/command.dag @@ -107,6 +107,9 @@ fn argv_command(program: ProgramIdentity, arguments: List) -> ArgvComman decl_ref(module_path: "extdeps.tools.tar", decl_name: "tar_extract_command"), decl_ref(module_path: "extdeps.tools.tar", decl_name: "tar_create_from_list_command"), decl_ref(module_path: "extdeps.tools.tar", decl_name: "tar_list_command"), + decl_ref(module_path: "extdeps.tools.tar", decl_name: "tar_create_from_two_lists_command"), + decl_ref(module_path: "extdeps.tools.tar", decl_name: "tar_list_sizes_command"), + decl_ref(module_path: "extdeps.tools.tar", decl_name: "tar_extract_listed_command"), decl_ref(module_path: "extdeps.tools.btrfs_progs", decl_name: "btrfs_subvolume_create_command"), decl_ref(module_path: "extdeps.tools.btrfs_progs", decl_name: "btrfs_subvolume_snapshot_read_only_command"), decl_ref(module_path: "extdeps.tools.btrfs_progs", decl_name: "btrfs_subvolume_delete_command"), @@ -115,6 +118,7 @@ fn argv_command(program: ProgramIdentity, arguments: List) -> ArgvComman decl_ref(module_path: "extdeps.tools.findutils", decl_name: "find_regular_files_sha256_command"), decl_ref(module_path: "extdeps.tools.findutils", decl_name: "find_owner_executable_files_command"), decl_ref(module_path: "extdeps.tools.findutils", decl_name: "find_symlink_targets_command"), + decl_ref(module_path: "extdeps.tools.findutils", decl_name: "find_regular_file_size_mtime_command"), decl_ref(module_path: "extdeps.tools.gnu_coreutils", decl_name: "mv_exact_destination_command"), decl_ref(module_path: "extdeps.tools.gnu_coreutils", decl_name: "sort_in_place_command"), decl_ref(module_path: "extdeps.tools.unzip", decl_name: "unzip_extract_command"), diff --git a/dag/extdeps/languages/rust/emit.dag b/dag/extdeps/languages/rust/emit.dag index 2bffcb57d7d..69034eef1fa 100644 --- a/dag/extdeps/languages/rust/emit.dag +++ b/dag/extdeps/languages/rust/emit.dag @@ -76,8 +76,8 @@ data rust_simple_method_specs: List = [ { method_name: "first", template: "\{recv\}.first().cloned()", wraps_result: false }, { method_name: "enumerate", template: "\{recv\}.iter().cloned().enumerate().map(|(i, v)| (i as i64, v)).collect::>()", wraps_result: true }, { method_name: "chars", template: "\{recv\}.chars().map(|c| c as i64).collect::>()", wraps_result: true }, - { method_name: "skip", template: "\{recv\}.iter().cloned().skip(\{arg\} as usize).collect::>()", wraps_result: true }, - { method_name: "take", template: "\{recv\}.iter().cloned().take(\{arg\} as usize).collect::>()", wraps_result: true }, + { method_name: "skip", template: "v1_rt::list_skip(&\{recv\}, \{arg\})", wraps_result: true }, + { method_name: "take", template: "v1_rt::list_take(&\{recv\}, \{arg\})", wraps_result: true }, { method_name: "is_empty", template: "\{recv\}.is_empty()", wraps_result: false } ] diff --git a/dag/extdeps/tools/coreutils_truncate.dag b/dag/extdeps/tools/coreutils_truncate.dag new file mode 100644 index 00000000000..9c2a3072cec --- /dev/null +++ b/dag/extdeps/tools/coreutils_truncate.dag @@ -0,0 +1,29 @@ +module extdeps.tools.coreutils_truncate + +import extdeps.external_authority { ExternalAuthority } +import extdeps.uri { Uri, Https } +import std.types { Bool, NonEmptyStr, Unit } + +data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { + uri: Uri { + scheme: Https + locator: "www.gnu.org/software/coreutils/manual/html_node/truncate-invocation.html" + } +} + +// `truncate -s N FILE` creates FILE if it is absent and sets its size to EXACTLY N bytes; bytes it +// adds read as zero (coreutils "truncate invocation"). -s takes a bare decimal byte count here, never +// a suffixed one, so the size is the caller's integer and nothing the tool reinterprets. -- ends the +// options, so a path beginning with '-' is a path. +service coreutils.Truncate { + operation ToSize { + requires none + input { size_bytes: NonEmptyStr, path: NonEmptyStr } + output { success: Bool from "exit_success" } + transport shell { argv: ["truncate", "-s", "{size_bytes}", "--", "{path}"] } + exit { + 0 => Unit + nonzero => String "truncate could not size the path" + } + } +} diff --git a/dag/extdeps/tools/findutils.dag b/dag/extdeps/tools/findutils.dag index 971008edf0f..595cd5faeb4 100644 --- a/dag/extdeps/tools/findutils.dag +++ b/dag/extdeps/tools/findutils.dag @@ -101,3 +101,27 @@ fn find_owner_executable_files_command(root: String) -> ArgvCommand { fn find_symlink_targets_command(root: String) -> ArgvCommand { argv_command(program: find_program(), arguments: [root, "-type", "l", "-printf", "l %P -> %l\\n"]) } + +// THE REGULAR FILES UNDER A ROOT WITH THEIR SIZE AND MODIFICATION TIME, ONE +// `\t\t` LINE EACH, AND NOTHING READ BUT METADATA. Every -printf +// directive here is answered from the inode find already holds from its own stat(2) of the entry +// (find manual, "Format Directives"): %s is st_size in bytes, %T@ is st_mtime as seconds since the +// epoch with a fractional part, %P is the path with the starting point removed. No directive opens +// the file, and the expression carries no action that runs another program, so the command reads +// no file content -- find_command_runs_another_program below is how a consumer checks the second +// half on the value rather than on this note. The path is LAST so a name holding a tab survives as +// the rest of the line; the escapes are find's own, so the argv word carries a backslash and a +// letter, never a control character. +fn find_regular_file_size_mtime_command(root: String) -> ArgvCommand { + argv_command(program: find_program(), arguments: [root, "-type", "f", "-printf", "%s\\t%T@\\t%P\\n"]) +} + +// THE ACTIONS THAT MAKE find RUN ANOTHER PROGRAM ON WHAT IT MATCHED (find manual, "Run Commands"): +// -exec and -execdir, and their prompting forms -ok and -okdir. find itself reads directory entries +// and inodes; every content reader it can reach -- cat, sha256sum, grep -- arrives through one of +// these four. find_regular_files_sha256_command above is a real builder that uses one. +data find_program_running_actions: List = ["-exec", "-execdir", "-ok", "-okdir"] + +fn find_command_runs_another_program(command: ArgvCommand) -> Bool { + any(command.arguments, a => any(find_program_running_actions, x => a == x)) +} diff --git a/dag/extdeps/tools/gzip.dag b/dag/extdeps/tools/gzip.dag index a2a3e845cb9..d0a78421957 100644 --- a/dag/extdeps/tools/gzip.dag +++ b/dag/extdeps/tools/gzip.dag @@ -58,3 +58,19 @@ service gzip.Gzip { } } } + +// COMPRESS ONE FILE BESIDE ITSELF: `--keep --best --force`, which writes .gz at level 9, keeps +// the input, and replaces a stale .gz (gzip manual, "Invoking gzip"). The compressed size is a +// stat of .gz; it is how a caller asks whether bytes are compressible without carrying them. +service gzip.Compress { + operation BesideKeepBest { + requires none + input { path: NonEmptyStr } + output { success: Bool from "exit_success" } + transport shell { argv: ["gzip", "--keep", "--best", "--force", "--", "{path}"] } + exit { + 0 => Unit + nonzero => String "gzip could not compress the path" + } + } +} diff --git a/dag/extdeps/tools/openssl.dag b/dag/extdeps/tools/openssl.dag index 18b7fff6875..76a38bbfe9e 100644 --- a/dag/extdeps/tools/openssl.dag +++ b/dag/extdeps/tools/openssl.dag @@ -113,3 +113,28 @@ data extdeps_model_scope: ExternalModelScope = ExternalModelScope { first_citation: extdeps.tools.openssl.extdeps_external_authority_anchor, further_citations: [] } + +// `openssl enc -aes-256-ctr` ENCRYPTS ONE FILE INTO ANOTHER UNDER AES-256 IN COUNTER MODE (openssl-enc, +// docs.openssl.org/3.0/man1/openssl-enc/). CTR is a stream mode, so the output is exactly as long as +// the input -- no padding, and -nosalt drops the "Salted__" header -- and is the input XOR the AES +// keystream. -pass pass:

derives the key and IV from the passphrase; -md sha256 pins that +// derivation's digest, which is the default from OpenSSL 1.1.0 on, so 1.1 and 3.x hosts agree. The +// passphrase is in argv and is therefore NOT a secret: this operation is for keyed, reproducible +// streams whose key anyone may know (extdeps.crypto.seeded_filler), never for confidentiality. +service openssl.Enc { + operation Aes256CtrPassphrase { + requires none + input { passphrase: NonEmptyStr, input_path: NonEmptyStr, output_path: NonEmptyStr } + output { + success: Bool from "exit_success" + stderr: String from "stderr" + } + transport shell { + argv: [openssl_program().invocation, "enc", "-aes-256-ctr", "-nosalt", "-md", "sha256", "-pass", "pass:{passphrase}", "-in", "{input_path}", "-out", "{output_path}"] + } + exit { + 0 => Unit + nonzero => String "openssl enc refused" + } + } +} diff --git a/dag/extdeps/tools/tar.dag b/dag/extdeps/tools/tar.dag index 893dccb3a62..3e403b89b07 100644 --- a/dag/extdeps/tools/tar.dag +++ b/dag/extdeps/tools/tar.dag @@ -69,3 +69,35 @@ fn tar_create_from_list_command(archive: String, from_directory: String, member_ fn tar_list_command(archive: String) -> ArgvCommand { argv_command(program: tar_program(), arguments: ["-tf", archive]) } + +// CREATE FROM TWO MEMBER LISTS, EACH READ RELATIVE TO ITS OWN DIRECTORY, INTO ONE ARCHIVE. GNU tar +// applies -C to every member named after it, including members read from a -T file ("Changing the +// Working Directory" in the GNU tar manual), so the second list's members are taken from the second +// directory under the SAME member names the list spells. That is how a staged tree stands in for +// paths of the first: a member staged at / is archived as . The flags are +// tar_create_from_list_command's, with the same reason for each. +fn tar_create_from_two_lists_command(archive: String, first_directory: String, first_list: String, second_directory: String, second_list: String) -> ArgvCommand { + argv_command( + program: tar_program(), + arguments: ["-cf", archive, "--no-recursion", "--verbatim-files-from", "-C", first_directory, "-T", first_list, "-C", second_directory, "-T", second_list], + ) +} + +// LIST AN ARCHIVE'S MEMBERS WITH THEIR SIZES: `--list --verbose --numeric-owner --full-time +// --quoting-style=literal`. Each line is the type-and-mode word, uid/gid, the size in bytes, the +// date, the time, then the member name unescaped (GNU tar manual, "The Verbose Listing" and +// "Quoting Member Names"); numeric ids and full time keep each of the five leading fields a single +// word, so the name is whatever follows the fifth. +fn tar_list_sizes_command(archive: String) -> ArgvCommand { + argv_command(program: tar_program(), arguments: ["--list", "--verbose", "--numeric-owner", "--full-time", "--quoting-style=literal", "-f", archive]) +} + +// EXTRACT EXACTLY THE LISTED MEMBERS INTO A DIRECTORY: -C first, so every member lands beneath it +// under its archived name; -T names the members; --no-recursion and --verbatim-files-from as on +// create. A listed name the archive does not hold makes tar exit nonzero. +fn tar_extract_listed_command(archive: String, into_directory: String, member_list: String) -> ArgvCommand { + argv_command( + program: tar_program(), + arguments: ["-xf", archive, "-C", into_directory, "--no-recursion", "--verbatim-files-from", "-T", member_list], + ) +} diff --git a/dag/gunbc/ci/ci_layer_roots.dag b/dag/gunbc/ci/ci_layer_roots.dag index 78617e3ebb5..0a0a6bc7089 100644 --- a/dag/gunbc/ci/ci_layer_roots.dag +++ b/dag/gunbc/ci/ci_layer_roots.dag @@ -534,6 +534,21 @@ data witness_exclusion_frontier: List = [ classification: LocalRepoWetLane, reason: excl_local_repo_wet_digest_stdin_reason, dissolution: excl_local_repo_wet_digest_stdin_dissolve}, + WitnessExclusionRow { + pattern: "workspace_source_pack_archive_real_execution_witness_test.dag", + classification: LocalRepoWetLane, + reason: excl_local_repo_wet_tempdir_write_reason, + dissolution: excl_local_repo_wet_tempdir_write_dissolve}, + WitnessExclusionRow { + pattern: "seeded_filler_real_execution_witness_test.dag", + classification: LocalRepoWetLane, + reason: excl_local_repo_wet_tempdir_write_reason, + dissolution: excl_local_repo_wet_tempdir_write_dissolve}, + WitnessExclusionRow { + pattern: "workspace_transcript_rate_real_execution_witness_test.dag", + classification: LocalRepoWetLane, + reason: excl_local_repo_wet_tempdir_write_reason, + dissolution: excl_local_repo_wet_tempdir_write_dissolve}, WitnessExclusionRow { pattern: "megarac_spx_ui_surface_artifact_integrity_witness_test.dag", classification: LocalRepoWetLane, diff --git a/dag/gunbc/ci/ci_spec.dag b/dag/gunbc/ci/ci_spec.dag index d86d550ed34..ce65488ec28 100644 --- a/dag/gunbc/ci/ci_spec.dag +++ b/dag/gunbc/ci/ci_spec.dag @@ -78,6 +78,7 @@ import gunbc.machine_intake_mtcollins1_sol_notice { mtcollins1_sol_notice_stop_s import gunbc.machine_intake_mtcollins1_fan_observe { mtcollins1_fan_credential_path_env, mtcollins1_fan_observation_receipt_path } import gunbc.fabric_writer_identity_observe { fabric_writer_identity_receipt_path } import gunbc.fabric.workspace_source { workspace_source_receipt_path } +import gunbc.fabric.workspace_transcript_rate { workspace_transcript_rate_receipt_path } import gunbc.fabric.workspace_checkpoint_instrument { workspace_measure_receipt_path, workspace_restore_receipt_path } import gunbc.machine_intake_mtcollins1_census_image_publish { mtcollins1_census_image_receipt_path } import gunbc.machine_intake_mtcollins1_census_member_readback { census_member_readback_receipt_path } @@ -856,6 +857,11 @@ data gunbc_ci_workspace_checkpoint_restore_target: GunbcRunStepTarget = GunbcRun function: "workspace_checkpoint_restore_wet", } +data gunbc_ci_workspace_transcript_rate_observe_target: GunbcRunStepTarget = GunbcRunStepTarget { + entry: "dag/gunbc/fabric/workspace_transcript_rate.dag", + function: "workspace_transcript_rate_observe_wet", +} + // THE MICRO-VM GUEST IMAGE HAS TWO TARGETS BECAUSE OBSERVE AND CONVERGE ARE TWO OPERATIONS, and // this repository's ordering law is what keeps them apart: an observation reports a host's standing // and repairs nothing, so it may run anywhere at any time; a converge builds an image on the host it @@ -1397,6 +1403,7 @@ fn gunbc_run_step_targets() -> List { gunbc_ci_workspace_source_pack_target, gunbc_ci_workspace_checkpoint_measure_target, gunbc_ci_workspace_checkpoint_restore_target, + gunbc_ci_workspace_transcript_rate_observe_target, gunbc_ci_runner_guest_image_observe_target, gunbc_ci_runner_guest_image_converge_target, gunbc_ci_runner_microvm_host_converge_target, @@ -2764,6 +2771,16 @@ fn gunbc_ci_workspace_checkpoint_restore_invoke() -> String { ) } +fn gunbc_ci_workspace_transcript_rate_observe_invoke() -> String { + gunbc_run_step_script( + source_roots: witness_layer_roots, + entry: gunbc_ci_workspace_transcript_rate_observe_target.entry, + function: gunbc_ci_workspace_transcript_rate_observe_target.function, + claim_run: false, + receipt_rel: workspace_transcript_rate_receipt_path + ) +} + fn gunbc_ci_runner_guest_image_observe_invoke() -> String { gunbc_run_step_script( source_roots: witness_layer_roots, diff --git a/dag/gunbc/fabric/workspace_checkpoint_instrument.dag b/dag/gunbc/fabric/workspace_checkpoint_instrument.dag index 26834bbad79..2d90bd63902 100644 --- a/dag/gunbc/fabric/workspace_checkpoint_instrument.dag +++ b/dag/gunbc/fabric/workspace_checkpoint_instrument.dag @@ -62,7 +62,7 @@ import gunbc.fabric.fabric_object_r2 { put_fabric_object_r2, get_fabric_object_r2, FabricObjectR2PutVerdict, FabricObjectR2Stored, FabricObjectR2PresentVerified, FabricObjectR2OccupiedByOther, FabricObjectR2Refused, FabricObjectR2OutcomeUnknown, } -import gunbc.fabric.workspace_source { fabric_workspace_source_key, workspace_put_words, workspace_put_landed } +import gunbc.fabric.workspace_source { fabric_workspace_source_key, workspace_put_words, workspace_put_landed, workspace_transcript_caveat } import gunbc.fabric_storage_r2_head { R2HeadBinding, R2HeadCredential, r2_head_binding, r2_head_read, r2_head_advance, r2_head_fabric_advance, R2HeadAdvanceResult, R2HeadAdvanceKnown, R2HeadAdvanceOutcomeUnknown, R2HeadStagingCustody, r2_head_staging_of, @@ -204,19 +204,39 @@ fn workspace_part_words(p: WorkspaceMeasuredPart) -> String { } } -fn workspace_bound_words(name: String, parts: List, v: WorkspaceBoundVerdict) -> String { - let inputs = join(map(parts, p => workspace_part_words(p: p)), " + ") - match v { - BoundHolds { total: t, bound: b } => join([name, "=holds total=", workspace_ms_words(n: t), " bound=", workspace_ms_words(n: b), " inputs: ", inputs], "") - BoundExceeded { at_least: t, bound: b, complete: c } => join([name, "=exceeded at_least=", workspace_ms_words(n: t), " bound=", workspace_ms_words(n: b), if c { " complete" } else { " by-measured-parts-alone" }, " inputs: ", inputs], "") - BoundUndecided { measured: t, bound: b, missing: m } => join([name, "=undecided measured=", workspace_ms_words(n: t), " bound=", workspace_ms_words(n: b), " missing: ", join(m, "; "), " inputs: ", inputs], "") - } +// THE BOUND VERDICTS ARE WITHHELD WHILE THE SUBJECT IS A STATIC TREE WITH SYNTHETIC TRANSCRIPTS. +// The source pack (gunbc.fabric.workspace_source) carries transcript content as same-size +// placeholders and the tree does not change while it is measured, so a time over it says nothing +// about checkpointing a workspace whose transcripts are being appended: the 10 s target and the 60 s +// window are not decided by this run, holding or exceeded, and each line says so beside its measured +// inputs. The quota is a size, and the placeholders preserve size, so the 10 GB quota is decided -- +// over the static tree, and labelled static. workspace_bound_verdict above is the fold these lines +// will print once a churn phase measures appends (the later churn child of this lane); until then +// it is exercised by its witness and by nothing that prints. +fn workspace_withheld_bound_words(name: String, parts: List, bound: Nanosecond) -> String { + join([name, "=withheld: churn not measured static_measured=", workspace_ms_words(n: workspace_parts_measured(parts: parts)), " bound=", workspace_ms_words(n: bound), " inputs: ", join(map(parts, p => workspace_part_words(p: p)), " + ")], "") +} + +// THE DRAFT-NUMBER BLOCK OF THE MEASURE RECEIPT: the caveat, the four withheld bound lines, and the +// static quota line (or why it is undecided). +fn workspace_draft_verdict_lines(snapshot_parts: List, scan_parts: List, static_size: ByteSize?, n: WorkspaceDraftNumbers) -> String { + join([ + workspace_transcript_caveat as String, "\n", + workspace_withheld_bound_words(name: "snapshot_path_vs_target", parts: snapshot_parts, bound: n.checkpoint_target), "\n", + workspace_withheld_bound_words(name: "snapshot_path_vs_window", parts: snapshot_parts, bound: n.window), "\n", + workspace_withheld_bound_words(name: "scan_path_vs_target", parts: scan_parts, bound: n.checkpoint_target), "\n", + workspace_withheld_bound_words(name: "scan_path_vs_window", parts: scan_parts, bound: n.window), "\n", + match static_size { + Present { value: b } => workspace_quota_words(v: workspace_quota_verdict(size: b, quota: n.quota)) + Absent => "quota_static=undecided (the upload refused, so no byte total was established)" + }, "\n", + ], "") } fn workspace_quota_words(v: WorkspaceQuotaVerdict) -> String { match v { - QuotaHolds { size: n, quota: q } => join(["quota=holds bytes=", to_string(measure_count(m: n)), " quota_bytes=", to_string(measure_count(m: q))], "") - QuotaExceeded { size: n, quota: q } => join(["quota=exceeded bytes=", to_string(measure_count(m: n)), " quota_bytes=", to_string(measure_count(m: q))], "") + QuotaHolds { size: n, quota: q } => join(["quota_static=holds bytes=", to_string(measure_count(m: n)), " quota_bytes=", to_string(measure_count(m: q))], "") + QuotaExceeded { size: n, quota: q } => join(["quota_static=exceeded bytes=", to_string(measure_count(m: n)), " quota_bytes=", to_string(measure_count(m: q))], "") } } @@ -1008,9 +1028,9 @@ fn workspace_measure_report(workspace: NonEmptyStr, snapshot: NonEmptyStr, scrat join(["files=", to_string(length(es)), " bytes=", to_string(measure_count(m: b)), " chunks_uploaded=", to_string(st), " chunks_already_present=", to_string(oc), " symlinks_not_carried=", to_string(sl), " ", workspace_part_words(p: upload_timed)], "") WorkspaceUploadRefused { detail: d, custody: _ } => join(["upload=refused ", d], "") } - let quota_line = match upload { - WorkspaceUploaded { entries: _, stored: _, occupied: _, bytes: b, symlinks: _, custody: _ } => workspace_quota_words(v: workspace_quota_verdict(size: b, quota: n.quota)) - WorkspaceUploadRefused { detail: _, custody: _ } => "quota=undecided (the upload refused, so no byte total was established)" + let static_size = match upload { + WorkspaceUploaded { entries: _, stored: _, occupied: _, bytes: b, symlinks: _, custody: _ } => Present { value: b } + WorkspaceUploadRefused { detail: _, custody: _ } => none } let cleanup = join([ "cleanup snapshot=", if process_ok(run: workspace_run_local(command: btrfs_subvolume_delete_command(path: snapshot as String))) { "deleted" } else { "retained" }, @@ -1023,11 +1043,7 @@ fn workspace_measure_report(workspace: NonEmptyStr, snapshot: NonEmptyStr, scrat kopia_scan_words(scan: scan), "\n", upload_words, "\n", "commit=", match timed_commit.commit { Present { value: c } => workspace_commit_words(c: c) Absent => "not-attempted" }, " ", workspace_part_words(p: timed_commit.part), "\n", - workspace_bound_words(name: "snapshot_path_vs_target", parts: snapshot_parts, v: workspace_bound_verdict(parts: snapshot_parts, bound: n.checkpoint_target)), "\n", - workspace_bound_words(name: "snapshot_path_vs_window", parts: snapshot_parts, v: workspace_bound_verdict(parts: snapshot_parts, bound: n.window)), "\n", - workspace_bound_words(name: "scan_path_vs_target", parts: scan_parts, v: workspace_bound_verdict(parts: scan_parts, bound: n.checkpoint_target)), "\n", - workspace_bound_words(name: "scan_path_vs_window", parts: scan_parts, v: workspace_bound_verdict(parts: scan_parts, bound: n.window)), "\n", - quota_line, "\n", + workspace_draft_verdict_lines(snapshot_parts: snapshot_parts, scan_parts: scan_parts, static_size: static_size, n: n), cleanup, "\n", workspace_custody_words(c: run_custody), "\n", ], "") diff --git a/dag/gunbc/fabric/workspace_source.dag b/dag/gunbc/fabric/workspace_source.dag index ee02a550ef0..ab8f3a909fb 100644 --- a/dag/gunbc/fabric/workspace_source.dag +++ b/dag/gunbc/fabric/workspace_source.dag @@ -1,12 +1,13 @@ module gunbc.fabric.workspace_source -import std.types { String, NonEmptyStr, Int, Bool, List, FilePath } +import std.types { String, NonEmptyStr, Int, Bool, List, Map, FilePath } +import v2.std.collection { empty_map, map_insert, map_lookup, map_contains_key } import std.optional { Present, Absent } import std.algebra { Cons, Empty } import std.process { ProcessExit, ExitSuccess, exit_failure } import std.measure { ByteSize, byte_size, measure_count, nanosecond, nanosecond_count, ObservationInstant, ProcessMonotonic, WallClock } import std.checkpointed_workspace { WorkspaceAgeOf, WorkspaceAgeUndefined, workspace_age_between } -import std.content_hash { Sha256Digest } +import std.content_hash { Sha256Digest, sha256_hex_digest } import std.fabric_blob { FabricBlobAddress, FabricBlobReading, fabric_blob_object_name, fabric_blob_reading_digest, fabric_blob_reading_size, FabricBlobRead, FabricBlobReadRefused, read_staged_fabric_blob, fabric_blob_fault_text, @@ -16,7 +17,9 @@ import extdeps.filesystem.filesystem_io { Filesystem } import extdeps.shell import extdeps.shell.exec { ProcessRunOutcome, ProcessRan, ProcessNotSpawned } import extdeps.exec.command { CommandTransport, LocalExec } -import extdeps.tools.tar { tar_create_from_list_command, tar_list_command } +import extdeps.tools.tar { tar_create_from_two_lists_command, tar_list_command, tar_list_sizes_command, tar_extract_listed_command } +import extdeps.tools.sha256sum +import extdeps.crypto.seeded_filler { seeded_filler_write, SeededFillerWritten, SeededFillerRefused, seeded_filler_step_words } import gunbc.command_runner { run_shell_command_observe_outcome } import gunbc.auth.access_token_source { AccessTokenSource, AccessTokenSourceSelected, AccessTokenSourceUnavailable, select_access_token_source } import gunbc.cloudflare.r2_origin { FabricWorkspace } @@ -33,8 +36,10 @@ import gunbc.host_effect { LocalShell } // directory. No session holds a key that reaches both, and none is minted for this: the route is two // dispatched jobs through the workspace bucket. This module is the first job. On srv1 it enumerates // the source roots as the runner's own principal, drops every path the credential exclusion names -// and every build output, packs exactly what remains into one tar from that list, reads the tar's -// membership back and refuses if any member is excluded, and puts the tar into the workspace bucket +// and every build output, stands a same-size, same-path synthetic placeholder in for every +// transcript file, packs the real members and the placeholders into one tar, reads the tar's +// membership and sizes back and refuses if any credential is carried or any transcript member is not +// its placeholder at its planned size, and puts the tar into the workspace bucket // under the SHA-256 of its bytes. The srv3 job is dispatched with that address and fetches it. // // A ROOT THE RUNNER CANNOT READ REFUSES, NAMING WHAT IS MISSING. The enumeration runs as the job @@ -53,16 +58,76 @@ type WorkspaceSourceRoot { } // THE SRV1 HOST PATHS, pinned rather than guessed (warm-badger-442, 2026-10-05, read from a session's -// mountinfo on node srv1). Agent state is per auth slot on the host -- /home/briansrls/.claude@, -// mounted at /session-home/.claude inside session containers -- so the slot is part of the declared -// input: @gmail3 is the slot whose state the 2026-10-02 figures measured. The Codex root is its home -// directory; its auth file is excluded as a credential and its sessions and history as transcripts. -data workspace_source_roots: List = [ +// mountinfo on node srv1). The clone and the Codex home are fixed; the Codex auth file is excluded as +// a credential and its sessions and history as transcripts. +data workspace_source_fixed_roots: List = [ WorkspaceSourceRoot { label: "gunbc-clone", path: "/home/briansrls/gunbc", build_output: ["target/"] }, - WorkspaceSourceRoot { label: "claude-agent-state-gmail3", path: "/home/briansrls/.claude@gmail3", build_output: [] }, WorkspaceSourceRoot { label: "codex-state", path: "/home/briansrls/.codex", build_output: [] }, ] +// ── THE AGENT-STATE ROOT: A DECLARED INPUT, ADMITTED AGAINST WHAT EXISTS (warm-badger-442) ─────── +// +// Claude agent state lives per auth slot, /home/briansrls/.claude@ (mounted at +// /session-home/.claude inside session containers), and the operator's credential swaps move WHICH +// slot is active -- so the slot is not a constant of this module. It is the dispatch input +// agent_state_slot (environment WORKSPACE_AGENT_STATE_SLOT), and it is admitted only when it names +// one of the .claude@* directories observed in the home at pack time: a closed choice over what +// exists. There is no default and no fallback to "the active one": an absent or unknown slot refuses, +// naming the observed slots, and the admitted slot and its path are printed in the receipt. +data workspace_agent_state_slot_env: NonEmptyStr = "WORKSPACE_AGENT_STATE_SLOT" + +data workspace_agent_state_home: NonEmptyStr = "/home/briansrls" + +data workspace_agent_state_prefix: NonEmptyStr = ".claude@" + +type WorkspaceAgentStateSlot + = AgentStateSlotAdmitted { slot: NonEmptyStr, root: WorkspaceSourceRoot } + | AgentStateSlotAbsent { observed: List } + | AgentStateSlotUnknown { requested: String, observed: List } + +// THE SLOTS A HOME LISTING HOLDS: each directory whose last segment is .claude@, nonempty. +fn workspace_observed_agent_state_slots(dirs: List) -> List { + flat_map(dirs, d => { + let last = match reverse(split(d, separator: "/")) { Cons { head: h, tail: _ } => h Empty => "" } + let prefix = workspace_agent_state_prefix as String + if starts_with(s: last, prefix: prefix) && length(last) > length(prefix) { [substring(s: last, start: length(prefix), end: length(last))] } else { [] } + }) +} + +fn workspace_agent_state_slot_admission(requested: String?, observed: List) -> WorkspaceAgentStateSlot { + match requested { + Absent => AgentStateSlotAbsent { observed: observed } + Present { value: r } => + if r == "" { + AgentStateSlotAbsent { observed: observed } + } else if any(observed, o => o == r) { + AgentStateSlotAdmitted { + slot: r as NonEmptyStr, + root: WorkspaceSourceRoot { label: concat("claude-agent-state-", r) as NonEmptyStr, path: join([workspace_agent_state_home as String, "/", workspace_agent_state_prefix as String, r], "") as NonEmptyStr, build_output: [] }, + } + } else { + AgentStateSlotUnknown { requested: r, observed: observed } + } + } +} + +fn workspace_agent_state_slot_words(admission: WorkspaceAgentStateSlot) -> String { + match admission { + AgentStateSlotAdmitted { slot: s, root: r } => join(["agent_state_slot=", s as String, " path=", r.path as String], "") + AgentStateSlotAbsent { observed: o } => join(["agent_state_slot=refused-absent observed=", join(o, ","), " (", workspace_agent_state_slot_env as String, " names no slot; there is no default)"], "") + AgentStateSlotUnknown { requested: r, observed: o } => join(["agent_state_slot=refused-unknown requested=", r, " observed=", join(o, ","), " (no ", workspace_agent_state_home as String, "/", workspace_agent_state_prefix as String, r, " exists)"], "") + } +} + +fn workspace_agent_state_slot_observe() -> WorkspaceAgentStateSlot { + let requested = match shell.Env.Get(name: workspace_agent_state_slot_env).value { + Absent => none + Present { value: v } => Present { value: replace(v, "\n", "") } + } + let dirs = shell.Find.ListDirs(path: workspace_agent_state_home as FilePath) + workspace_agent_state_slot_admission(requested: requested, observed: workspace_observed_agent_state_slots(dirs: map(dirs.dirs, d => d as String))) +} + data workspace_source_pack_host: NonEmptyStr = "srv1" // ── THE CREDENTIAL EXCLUSION: ONE ROW, LISTED VERBATIM IN EVERY RECEIPT ──────────────────────── @@ -229,7 +294,7 @@ type WorkspaceSourceMemberKind = SourceFile | SourceSymlink type WorkspaceSourceLine = WorkspaceSourceMember { member: NonEmptyStr, kind: WorkspaceSourceMemberKind } - | WorkspaceSourceExcluded { member: NonEmptyStr, by: WorkspaceExclusion, bytes: ByteSize } + | WorkspaceSourceExcluded { member: NonEmptyStr, kind: WorkspaceSourceMemberKind, by: WorkspaceExclusion, bytes: ByteSize } | WorkspaceSourceBuildOutput { member: NonEmptyStr } | WorkspaceSourceUnparsed { line: String } @@ -256,19 +321,23 @@ fn workspace_source_line(root: WorkspaceSourceRoot, exclusions: List WorkspaceSourceMemberKind? { + if kind == "f" { Present { value: SourceFile } } else if kind == "l" { Present { value: SourceSymlink } } else { none } +} + fn workspace_source_line_sized(root: WorkspaceSourceRoot, exclusions: List, kind: String, relative: String, size: ByteSize, line: String) -> WorkspaceSourceLine { let member = workspace_root_member(root: root, relative: relative) as NonEmptyStr - match workspace_path_exclusion(exclusions: exclusions, path: member as String) { - Present { value: x } => WorkspaceSourceExcluded { member: member, by: x, bytes: size } - Absent => - if workspace_path_is_build_output(root: root, relative: relative) { - WorkspaceSourceBuildOutput { member: member } - } else if kind == "f" { - WorkspaceSourceMember { member: member, kind: SourceFile } - } else if kind == "l" { - WorkspaceSourceMember { member: member, kind: SourceSymlink } - } else { - WorkspaceSourceUnparsed { line: line } + match workspace_source_member_kind(kind: kind) { + Absent => WorkspaceSourceUnparsed { line: line } + Present { value: k } => + match workspace_path_exclusion(exclusions: exclusions, path: member as String) { + Present { value: x } => WorkspaceSourceExcluded { member: member, kind: k, by: x, bytes: size } + Absent => + if workspace_path_is_build_output(root: root, relative: relative) { + WorkspaceSourceBuildOutput { member: member } + } else { + WorkspaceSourceMember { member: member, kind: k } + } } } } @@ -283,6 +352,7 @@ type WorkspaceSourceTally { excluded_credential_bytes: ByteSize excluded_transcripts: Int excluded_transcript_bytes: ByteSize + transcript_placeholders: List build_output: Int unparsed: List } @@ -290,7 +360,7 @@ type WorkspaceSourceTally { // THE EXCLUDED SIZES OF ONE CLASS, partitioned by matching the class itself. fn workspace_excluded_sizes(lines: List, class: WorkspaceExclusionClass) -> List { flat_map(lines, l => match l { - WorkspaceSourceExcluded { member: _, by: x, bytes: b } => + WorkspaceSourceExcluded { member: _, kind: _, by: x, bytes: b } => match class { CredentialExclusion => match x.class { CredentialExclusion => [b] TranscriptExclusion => [] } TranscriptExclusion => match x.class { CredentialExclusion => [] TranscriptExclusion => [b] } @@ -301,6 +371,38 @@ fn workspace_excluded_sizes(lines: List, class: WorkspaceEx }) } +// ── TRANSCRIPT PLACEHOLDERS: THE SIZE AND PATH CARRIED, THE CONTENT NOT (operator, 2026-10-05) ─── +// +// Transcripts are the workspace's largest and highest-churn population, so dropping them would make +// stage 0 describe a cheaper workspace than the operator's. Each excluded transcript FILE is carried +// as a placeholder at the SAME member path with the SAME byte size, its bytes the seeded keystream +// of extdeps.crypto.seeded_filler keyed by the member path: incompressible, so neither kopia nor the +// chunk store's dedup sees less than the real content would cost, and reproducible, so two packs of +// one unchanged tree carry identical placeholders. A placeholder is minted ONLY from a line the +// TranscriptExclusion class excluded, and the match below has no arm that mints one from a +// credential, so a credential's path and size never appear in the archive. A transcript SYMLINK +// carries no content to stand in for and is excluded as before. What a placeholder cannot carry is +// stated, not implied: the content's compressibility and its churn; the receipt says so. +type WorkspaceTranscriptPlaceholder { + member: NonEmptyStr + size: ByteSize +} + +fn workspace_transcript_placeholders(lines: List) -> List { + flat_map(lines, l => match l { + WorkspaceSourceExcluded { member: m, kind: k, by: x, bytes: b } => + match x.class { + CredentialExclusion => [] + TranscriptExclusion => match k { SourceFile => [WorkspaceTranscriptPlaceholder { member: m, size: b }] SourceSymlink => [] } + } + WorkspaceSourceMember { member: _, kind: _ } => [] + WorkspaceSourceBuildOutput { member: _ } => [] + WorkspaceSourceUnparsed { line: _ } => [] + }) +} + +data workspace_transcript_caveat: NonEmptyStr = "transcript content synthetic, sizes preserved, static tree; NOT a 10 s / 60 s verdict" + fn workspace_bytes_total(sizes: List) -> ByteSize { byte_size(count: fold(sizes, init: 0, f: (acc, b) => acc + measure_count(m: b))) } @@ -310,16 +412,16 @@ fn workspace_source_tally(root: WorkspaceSourceRoot, exclusions: List match l { WorkspaceSourceMember { member: m, kind: _ } => [m] - WorkspaceSourceExcluded { member: _, by: _, bytes: _ } => [] + WorkspaceSourceExcluded { member: _, kind: _, by: _, bytes: _ } => [] WorkspaceSourceBuildOutput { member: _ } => [] WorkspaceSourceUnparsed { line: _ } => [] }), - files: length(filter(lines, l => match l { WorkspaceSourceMember { member: _, kind: SourceFile } => true WorkspaceSourceMember { member: _, kind: SourceSymlink } => false WorkspaceSourceExcluded { member: _, by: _, bytes: _ } => false WorkspaceSourceBuildOutput { member: _ } => false WorkspaceSourceUnparsed { line: _ } => false })), - symlinks: length(filter(lines, l => match l { WorkspaceSourceMember { member: _, kind: SourceSymlink } => true WorkspaceSourceMember { member: _, kind: SourceFile } => false WorkspaceSourceExcluded { member: _, by: _, bytes: _ } => false WorkspaceSourceBuildOutput { member: _ } => false WorkspaceSourceUnparsed { line: _ } => false })), - excluded: length(filter(lines, l => match l { WorkspaceSourceExcluded { member: _, by: _, bytes: _ } => true WorkspaceSourceMember { member: _, kind: _ } => false WorkspaceSourceBuildOutput { member: _ } => false WorkspaceSourceUnparsed { line: _ } => false })), + files: length(filter(lines, l => match l { WorkspaceSourceMember { member: _, kind: SourceFile } => true WorkspaceSourceMember { member: _, kind: SourceSymlink } => false WorkspaceSourceExcluded { member: _, kind: _, by: _, bytes: _ } => false WorkspaceSourceBuildOutput { member: _ } => false WorkspaceSourceUnparsed { line: _ } => false })), + symlinks: length(filter(lines, l => match l { WorkspaceSourceMember { member: _, kind: SourceSymlink } => true WorkspaceSourceMember { member: _, kind: SourceFile } => false WorkspaceSourceExcluded { member: _, kind: _, by: _, bytes: _ } => false WorkspaceSourceBuildOutput { member: _ } => false WorkspaceSourceUnparsed { line: _ } => false })), + excluded: length(filter(lines, l => match l { WorkspaceSourceExcluded { member: _, kind: _, by: _, bytes: _ } => true WorkspaceSourceMember { member: _, kind: _ } => false WorkspaceSourceBuildOutput { member: _ } => false WorkspaceSourceUnparsed { line: _ } => false })), excluded_credentials: length(workspace_excluded_sizes(lines: lines, class: CredentialExclusion)), excluded_credential_members: flat_map(lines, l => match l { - WorkspaceSourceExcluded { member: m, by: x, bytes: _ } => match x.class { CredentialExclusion => [m] TranscriptExclusion => [] } + WorkspaceSourceExcluded { member: m, kind: _, by: x, bytes: _ } => match x.class { CredentialExclusion => [m] TranscriptExclusion => [] } WorkspaceSourceMember { member: _, kind: _ } => [] WorkspaceSourceBuildOutput { member: _ } => [] WorkspaceSourceUnparsed { line: _ } => [] @@ -327,37 +429,154 @@ fn workspace_source_tally(root: WorkspaceSourceRoot, exclusions: List match l { WorkspaceSourceBuildOutput { member: _ } => true WorkspaceSourceMember { member: _, kind: _ } => false WorkspaceSourceExcluded { member: _, by: _, bytes: _ } => false WorkspaceSourceUnparsed { line: _ } => false })), + transcript_placeholders: workspace_transcript_placeholders(lines: lines), + build_output: length(filter(lines, l => match l { WorkspaceSourceBuildOutput { member: _ } => true WorkspaceSourceMember { member: _, kind: _ } => false WorkspaceSourceExcluded { member: _, kind: _, by: _, bytes: _ } => false WorkspaceSourceUnparsed { line: _ } => false })), unparsed: flat_map(lines, l => match l { WorkspaceSourceUnparsed { line: t } => [t] WorkspaceSourceMember { member: _, kind: _ } => [] - WorkspaceSourceExcluded { member: _, by: _, bytes: _ } => [] + WorkspaceSourceExcluded { member: _, kind: _, by: _, bytes: _ } => [] WorkspaceSourceBuildOutput { member: _ } => [] }), } } -// ── THE READBACK: AN ARCHIVE MEMBER THE EXCLUSION NAMES REFUSES THE UPLOAD ───────────────────── +// ── THE READBACK: WHAT THE ARCHIVE ACTUALLY HOLDS, BEFORE ANY BYTE LEAVES THE HOST ───────────── // -// The pack is built from the filtered list, so an excluded path should never be in it; this reads -// what the archive ACTUALLY holds (tar -tf) and runs every member through the same matcher. It is the -// check that the realization did what the list said -- a tar that recursed, or a list that was not -// the one written -- and its red is the run's refusal, before any byte leaves the host. +// The pack is built from the filtered lists, so a credential should never be in it and a transcript +// should be there only as its placeholder. That is provenance by construction, and it is NOT enough +// at a confidentiality boundary: a name and a size are exactly what a real transcript shares with +// its placeholder. So this reads what the archive ACTUALLY holds and refuses otherwise. Names come +// from `tar -tf` (archive_listing) and run through the same matcher: a credential-class name refuses +// outright, and a transcript-class name refuses unless it is a staged placeholder. Every staged +// placeholder must then be, IN THE ARCHIVE, a regular file (the verbose listing's own type letter, +// size_listing) of its planned size whose bytes -- extracted back out of the archive and hashed, +// archived_digests -- are the SHA-256 the filler produced when it was staged. A real transcript of +// the same size differs in digest and refuses. Each failure is its own typed refusal. +type WorkspaceStagedPlaceholder { + member: NonEmptyStr + size: ByteSize + expected_digest: Sha256Digest +} + type WorkspacePackReadback = WorkspacePackClean { members: Int } | WorkspacePackCarriesExcluded { member: NonEmptyStr, by: WorkspaceExclusion } + | WorkspacePackPlaceholderAbsent { member: NonEmptyStr } + | WorkspacePackPlaceholderNotRegular { member: NonEmptyStr, type_letter: String } + | WorkspacePackPlaceholderSizeDiffers { member: NonEmptyStr, planned: ByteSize, archived: ByteSize } + | WorkspacePackPlaceholderUnread { member: NonEmptyStr } + | WorkspacePackPlaceholderDigestDiffers { member: NonEmptyStr, expected: Sha256Digest, archived: String } -fn workspace_pack_readback(exclusions: List, archive_listing: String) -> WorkspacePackReadback { - workspace_pack_readback_lines(exclusions: exclusions, lines: filter(split(archive_listing, separator: "\n"), l => l != ""), seen: 0) +fn workspace_pack_readback(exclusions: List, placeholders: List, archive_listing: String, size_listing: String, archived_digests: Map) -> WorkspacePackReadback { + let planned = fold(placeholders, init: empty_map(), f: (m, p) => map_insert(m: m, key: p.member as String, value: p.size)) + match workspace_pack_readback_lines(exclusions: exclusions, planned: planned, lines: filter(split(archive_listing, separator: "\n"), l => l != ""), seen: 0) { + WorkspacePackClean { members: n } => { + let archived = fold(flat_map(filter(split(size_listing, separator: "\n"), l => l != ""), l => workspace_archived_entry(line: l)), init: empty_map(), f: (m, a) => map_insert(m: m, key: a.member, value: a)) + workspace_pack_placeholder_checks(placeholders: placeholders, archived: archived, digests: archived_digests, members: n) + } + WorkspacePackCarriesExcluded { member: m, by: x } => WorkspacePackCarriesExcluded { member: m, by: x } + WorkspacePackPlaceholderAbsent { member: m } => WorkspacePackPlaceholderAbsent { member: m } + WorkspacePackPlaceholderNotRegular { member: m, type_letter: t } => WorkspacePackPlaceholderNotRegular { member: m, type_letter: t } + WorkspacePackPlaceholderSizeDiffers { member: m, planned: p, archived: a } => WorkspacePackPlaceholderSizeDiffers { member: m, planned: p, archived: a } + WorkspacePackPlaceholderUnread { member: m } => WorkspacePackPlaceholderUnread { member: m } + WorkspacePackPlaceholderDigestDiffers { member: m, expected: e, archived: a } => WorkspacePackPlaceholderDigestDiffers { member: m, expected: e, archived: a } + } } -fn workspace_pack_readback_lines(exclusions: List, lines: List, seen: Int) -> WorkspacePackReadback { +fn workspace_pack_readback_lines(exclusions: List, planned: Map, lines: List, seen: Int) -> WorkspacePackReadback { match lines { Empty => WorkspacePackClean { members: seen } Cons { head: l, tail: rest } => match workspace_path_exclusion(exclusions: exclusions, path: l) { - Present { value: x } => WorkspacePackCarriesExcluded { member: l as NonEmptyStr, by: x } - Absent => workspace_pack_readback_lines(exclusions: exclusions, lines: rest, seen: seen + 1) + Absent => workspace_pack_readback_lines(exclusions: exclusions, planned: planned, lines: rest, seen: seen + 1) + Present { value: x } => + match x.class { + CredentialExclusion => WorkspacePackCarriesExcluded { member: l as NonEmptyStr, by: x } + TranscriptExclusion => + if map_contains_key(m: planned, key: l) { + workspace_pack_readback_lines(exclusions: exclusions, planned: planned, lines: rest, seen: seen + 1) + } else { + WorkspacePackCarriesExcluded { member: l as NonEmptyStr, by: x } + } + } + } + } +} + +// ONE VERBOSE-LISTING LINE: its type letter (the first character of the mode word: '-' a regular +// file, 'l' a symlink, 'h' a hard link, ...), its size, and its name. Five leading words (mode, +// uid/gid, size, date, time), each possibly padded by several spaces; the name is the rest of the +// line verbatim, so a name with spaces survives. A symlink's or hard link's name carries its target +// after it (" -> t", " link to t"), so such a line never names a placeholder exactly and a planned +// member archived as one reads as absent -- refused either way. +type WorkspaceArchivedEntry { + member: String + type_letter: String + size: ByteSize +} + +fn workspace_archived_entry(line: String) -> List { + match workspace_verbose_fields(words: split(line, separator: " "), fields: [], wanted: 5) { + Absent => [] + Present { value: parsed } => + match parsed.fields { + Cons { head: mode, tail: Cons { head: _, tail: Cons { head: size_text, tail: _ } } } => + match parse_int(s: size_text) { + Absent => [] + Present { value: n } => if n < 0 || parsed.rest == "" { [] } else { [WorkspaceArchivedEntry { member: parsed.rest, type_letter: substring(s: mode, start: 0, end: 1), size: byte_size(count: n) }] } + } + Cons { head: _, tail: Cons { head: _, tail: Empty } } => [] + Cons { head: _, tail: Empty } => [] + Empty => [] + } + } +} + +type WorkspaceVerboseFields { + fields: List + rest: String +} + +fn workspace_verbose_fields(words: List, fields: List, wanted: Int) -> WorkspaceVerboseFields? { + if wanted == 0 { + Present { value: WorkspaceVerboseFields { fields: fields, rest: join(words, " ") } } + } else { + match words { + Empty => none + Cons { head: w, tail: rest } => + if w == "" { + workspace_verbose_fields(words: rest, fields: fields, wanted: wanted) + } else { + workspace_verbose_fields(words: rest, fields: concat(fields, [w]), wanted: wanted - 1) + } + } + } +} + +// Every side is keyed by member name, so the check is one lookup per placeholder rather than a scan +// of the archive per placeholder. +fn workspace_pack_placeholder_checks(placeholders: List, archived: Map, digests: Map, members: Int) -> WorkspacePackReadback { + match placeholders { + Empty => WorkspacePackClean { members: members } + Cons { head: p, tail: rest } => + match map_lookup(m: archived, key: p.member as String) { + Absent => WorkspacePackPlaceholderAbsent { member: p.member } + Present { value: a } => + if a.type_letter != "-" { + WorkspacePackPlaceholderNotRegular { member: p.member, type_letter: a.type_letter } + } else if measure_count(m: a.size) != measure_count(m: p.size) { + WorkspacePackPlaceholderSizeDiffers { member: p.member, planned: p.size, archived: a.size } + } else { + match map_lookup(m: digests, key: p.member as String) { + Absent => WorkspacePackPlaceholderUnread { member: p.member } + Present { value: d } => + if d != (p.expected_digest.hex as String) { + WorkspacePackPlaceholderDigestDiffers { member: p.member, expected: p.expected_digest, archived: d } + } else { + workspace_pack_placeholder_checks(placeholders: rest, archived: archived, digests: digests, members: members) + } + } + } } } } @@ -442,7 +661,7 @@ fn workspace_source_tallies(enumerations: List) -> L fn workspace_tally_line(e: WorkspaceSourceEnumeration) -> String { match e { WorkspaceSourceEnumerated { root: r, tally: t } => - join(["root ", r.label as String, " path=", r.path as String, " files=", to_string(t.files), " symlinks=", to_string(t.symlinks), " excluded=", to_string(t.excluded), " excluded_credentials=", to_string(t.excluded_credentials), " excluded_credential_bytes=", to_string(measure_count(m: t.excluded_credential_bytes)), " excluded_credential_paths=", join(map(t.excluded_credential_members, m => m as String), ","), " excluded_transcripts=", to_string(t.excluded_transcripts), " excluded_transcript_bytes=", to_string(measure_count(m: t.excluded_transcript_bytes)), " build_output=", to_string(t.build_output)], "") + join(["root ", r.label as String, " path=", r.path as String, " files=", to_string(t.files), " symlinks=", to_string(t.symlinks), " excluded=", to_string(t.excluded), " excluded_credentials=", to_string(t.excluded_credentials), " excluded_credential_bytes=", to_string(measure_count(m: t.excluded_credential_bytes)), " excluded_credential_paths=", join(map(t.excluded_credential_members, m => m as String), ","), " excluded_transcripts=", to_string(t.excluded_transcripts), " excluded_transcript_bytes=", to_string(measure_count(m: t.excluded_transcript_bytes)), " transcript_placeholders=", to_string(length(t.transcript_placeholders)), " build_output=", to_string(t.build_output)], "") WorkspaceSourceUnreadable { root: r, detail: _ } => join(["root ", r.label as String, " path=", r.path as String, " UNREADABLE"], "") } } @@ -471,7 +690,7 @@ fn workspace_source_receipt_head(enumerations: List) join([ "workspace_source_pack host=", workspace_source_pack_host as String, "\n", "credential_exclusions=", workspace_exclusion_spellings(exclusions: workspace_credential_exclusions), "\n", - "transcript_exclusions=", workspace_exclusion_spellings(exclusions: workspace_transcript_exclusions), "\n", + "transcript_exclusions=", workspace_exclusion_spellings(exclusions: workspace_transcript_exclusions), " (carried as same-size, same-path placeholders)\n", join(map(enumerations, e => workspace_tally_line(e: e)), "\n"), "\n", ], "") } @@ -500,12 +719,20 @@ fn workspace_source_pack_wet() -> ProcessExit { if host != (workspace_source_pack_host as String) { exit_failure(reason: join(["workspace_source_pack: the source lives on ", workspace_source_pack_host as String, " but this job runs on '", host, "'; dispatch it with host=", workspace_source_pack_host as String], "")) } else { - let enumerations = workspace_source_enumerate_all(roots: workspace_source_roots) - let head = workspace_source_receipt_head(enumerations: enumerations) - match workspace_source_refusals(enumerations: enumerations) { - Cons { head: _, tail: _ } => - workspace_source_finish(receipt: concat(head, "verdict=refused-source-unreadable\n"), outcome: exit_failure(reason: join(concat(["workspace_source_pack: a source root is unreadable by the runner; no permission is widened here:"], workspace_source_refusals(enumerations: enumerations)), "\n"))) - Empty => workspace_source_pack_readable(head: head, tallies: workspace_source_tallies(enumerations: enumerations)) + let slot = workspace_agent_state_slot_observe() + let slot_line = concat(workspace_agent_state_slot_words(admission: slot), "\n") + match slot { + AgentStateSlotAbsent { observed: _ } => workspace_source_finish(receipt: join(["workspace_source_pack host=", host, "\n", slot_line, "verdict=refused-agent-state-slot\n"], ""), outcome: exit_failure(reason: concat("workspace_source_pack: ", slot_line))) + AgentStateSlotUnknown { requested: _, observed: _ } => workspace_source_finish(receipt: join(["workspace_source_pack host=", host, "\n", slot_line, "verdict=refused-agent-state-slot\n"], ""), outcome: exit_failure(reason: concat("workspace_source_pack: ", slot_line))) + AgentStateSlotAdmitted { slot: _, root: agent } => { + let enumerations = workspace_source_enumerate_all(roots: concat(workspace_source_fixed_roots, [agent])) + let head = concat(workspace_source_receipt_head(enumerations: enumerations), slot_line) + match workspace_source_refusals(enumerations: enumerations) { + Cons { head: _, tail: _ } => + workspace_source_finish(receipt: concat(head, "verdict=refused-source-unreadable\n"), outcome: exit_failure(reason: join(concat(["workspace_source_pack: a source root is unreadable by the runner; no permission is widened here:"], workspace_source_refusals(enumerations: enumerations)), "\n"))) + Empty => workspace_source_pack_readable(head: head, tallies: workspace_source_tallies(enumerations: enumerations)) + } + } } } } @@ -515,44 +742,271 @@ fn workspace_source_pack_readable(head: String, tallies: List t.members) + let placeholders = flat_map(tallies, t => t.transcript_placeholders) + let placeholder_root = join([staging.path as String, "/placeholders"], "") + let head2 = concat(head, workspace_placeholder_words(placeholders: placeholders)) + match workspace_stage_placeholders(root: placeholder_root, placeholders: placeholders, staged: []) { + WorkspacePlaceholdersRefused { why: why } => { + let removed = shell.Remove.RecursiveForce(path: staging.path) + workspace_source_finish(receipt: join([head2, "verdict=refused-placeholder-unstaged staging_removed=", if removed.success { "yes" } else { "no" }, "\n"], ""), outcome: exit_failure(reason: concat("workspace_source_pack: a transcript placeholder could not be staged: ", why))) + } + WorkspacePlaceholdersStaged { placeholders: staged } => { + let pack_start = workspace_source_instant(label: "workspace-source-pack-start") + let built = workspace_pack_archive(staging: staging.path as String, members: flat_map(tallies, t => t.members), placeholders: staged, placeholder_root: placeholder_root) + let pack_span = workspace_source_span_words(start: pack_start, end_label: "workspace-source-pack-end") + match built { + WorkspacePackRefused { refusal: v, why: why } => workspace_source_refuse_archive(head: head2, staging: staging.path as String, refusal: v, why: why) + WorkspacePackBuilt { archive: archive, readback: r } => + match workspace_pack_readback_refusal(readback: r) { + Present { value: refusal } => workspace_source_refuse_archive(head: head2, staging: staging.path as String, refusal: refusal.refusal, why: refusal.why) + Absent => workspace_source_publish(head: concat(head2, join(["archive_members=", to_string(workspace_pack_clean_members(readback: r)), " placeholders_digest_checked=", to_string(length(staged)), " pack=", pack_span, "\n"], "")), archive: archive, staging: staging.path as String) + } + } + } + } + } +} + +fn workspace_placeholder_words(placeholders: List) -> String { + join([ + "transcript_placeholders files=", to_string(length(placeholders)), + " bytes=", to_string(measure_count(m: workspace_bytes_total(sizes: map(placeholders, p => p.size)))), + " filler=extdeps.crypto.seeded_filler seed=member-path\n", + workspace_transcript_caveat as String, "\n", + ], "") +} + +type WorkspacePlaceholderStaging + = WorkspacePlaceholdersStaged { placeholders: List } + | WorkspacePlaceholdersRefused { why: String } + +// EACH PLACEHOLDER AT /, its parent directories made first, and its SHA-256 taken from +// the staged bytes: that digest is what the archive readback requires the archived member to hash +// to. The first refusal stops the staging; nothing is packed from a partly staged tree. +fn workspace_stage_placeholders(root: String, placeholders: List, staged: List) -> WorkspacePlaceholderStaging { + match placeholders { + Empty => WorkspacePlaceholdersStaged { placeholders: staged } + Cons { head: p, tail: rest } => { + let path = join([root, "/", p.member as String], "") + let parent = join(workspace_all_but_last(xs: split(path, separator: "/")), "/") + let made = shell.Mkdir.Parents(path: parent as FilePath) + if made.success == false { + WorkspacePlaceholdersRefused { why: concat("mkdir -p failed: ", parent) } + } else { + match seeded_filler_write(seed: p.member, size: p.size, path: path as NonEmptyStr) { + SeededFillerRefused { path: _, step: st, detail: d } => WorkspacePlaceholdersRefused { why: join([p.member as String, ": ", seeded_filler_step_words(step: st), ": ", d], "") } + SeededFillerWritten { path: _, size: _ } => + match workspace_file_digest(path: path) { + Absent => WorkspacePlaceholdersRefused { why: concat("the staged placeholder could not be hashed: ", p.member as String) } + Present { value: d } => workspace_stage_placeholders(root: root, placeholders: rest, staged: concat(staged, [WorkspaceStagedPlaceholder { member: p.member, size: p.size, expected_digest: d }])) + } + } + } + } + } +} + +fn workspace_file_digest(path: String) -> Sha256Digest? { + let d = sha256sum.Sha256.DigestFile(path: path as NonEmptyStr) + if d.success == false { none } else { sha256_hex_digest(hex: substring(s: d.line, start: 0, end: 64)) } +} + +fn workspace_all_but_last(xs: List) -> List { + match xs { + Empty => [] + Cons { head: _, tail: Empty } => [] + Cons { head: x, tail: rest } => concat([x], workspace_all_but_last(xs: rest)) + } +} + +// ── THE ARCHIVE: BUILT FROM BOTH SOURCES, READ BACK, AND ITS PLACEHOLDERS EXTRACTED AND HASHED ── +// +// The real members are read relative to "/" and the placeholders relative to their staging root, +// under the same member names, into one tar. The readback then lists the archive twice (names, then +// types and sizes), extracts exactly the placeholder members into a fresh directory, and hashes each +// extracted file. Every step's failure is a typed refusal; the readback's verdict is returned whole +// so the caller words it. +// +// Two orderings carry the reasons. The placeholder root is made FIRST: the second -C must name a +// directory even when no transcript was planned, so a workspace with no transcripts packs through +// the same two-list route with an empty second list, and the staging directory the lists are written +// into exists whatever was staged. And the LOCATED refusals come before extraction: names, types and +// sizes are judged from the listings alone, with each placeholder's digest assumed, so a missing or +// malformed member is refused by name rather than as a failed extract of the whole list. +// WHY THE ARCHIVE STEP REFUSED, as a closed sum: the receipt's verdict word is rendered from it at the +// edge by workspace_pack_refusal_words, so a refusal kind cannot be misspelled or invented at a +// construction site. The readback arms carry the member they name. +type WorkspacePackRefusal + = PackRefusedPlaceholderRootUnmade + | PackRefusedMemberListUnwritten + | PackRefusedTarFailed + | PackRefusedReadbackFailed + | PackRefusedPlaceholderExtractFailed + | PackRefusedMemberCountMismatch + | PackRefusedExcludedMember { member: NonEmptyStr, rule: String } + | PackRefusedPlaceholderAbsent { member: NonEmptyStr } + | PackRefusedPlaceholderNotRegular { member: NonEmptyStr, type_letter: String } + | PackRefusedPlaceholderSize { member: NonEmptyStr, planned: ByteSize, archived: ByteSize } + | PackRefusedPlaceholderUnread { member: NonEmptyStr } + | PackRefusedPlaceholderDigest { member: NonEmptyStr, expected: String, archived: String } + +fn workspace_pack_refusal_words(refusal: WorkspacePackRefusal) -> String { + match refusal { + PackRefusedPlaceholderRootUnmade => "refused-placeholder-root-unmade" + PackRefusedMemberListUnwritten => "refused-member-list-unwritten" + PackRefusedTarFailed => "refused-pack-failed" + PackRefusedReadbackFailed => "refused-readback-failed" + PackRefusedPlaceholderExtractFailed => "refused-placeholder-extract-failed" + PackRefusedMemberCountMismatch => "refused-member-count-mismatch" + PackRefusedExcludedMember { member: m, rule: x } => join(["refused-excluded-member member=", m as String, " rule=", x], "") + PackRefusedPlaceholderAbsent { member: m } => concat("refused-placeholder-absent member=", m as String) + PackRefusedPlaceholderNotRegular { member: m, type_letter: t } => join(["refused-placeholder-not-regular member=", m as String, " type=", t], "") + PackRefusedPlaceholderSize { member: m, planned: p, archived: a } => join(["refused-placeholder-size member=", m as String, " planned=", to_string(measure_count(m: p)), " archived=", to_string(measure_count(m: a))], "") + PackRefusedPlaceholderUnread { member: m } => concat("refused-placeholder-unread member=", m as String) + PackRefusedPlaceholderDigest { member: m, expected: e, archived: a } => join(["refused-placeholder-digest member=", m as String, " expected=", e, " archived=", a], "") + } +} + +type WorkspacePackArchive + = WorkspacePackBuilt { archive: String, readback: WorkspacePackReadback } + | WorkspacePackRefused { refusal: WorkspacePackRefusal, why: String } + +fn workspace_pack_archive(staging: String, members: List, placeholders: List, placeholder_root: String) -> WorkspacePackArchive { + let list_path = join([staging, "/members"], "") + let placeholder_list_path = join([staging, "/placeholder-members"], "") + let archive = join([staging, "/workspace-source.tar"], "") + let root_made = shell.Mkdir.Parents(path: placeholder_root as FilePath) + if root_made.success == false { + WorkspacePackRefused { refusal: PackRefusedPlaceholderRootUnmade, why: concat("the placeholder staging root could not be made: ", placeholder_root) } + } else { let listed = Filesystem.Write(path: list_path, content: concat(join(map(members, m => m as String), "\n"), "\n")) - if listed.success == false { - workspace_source_finish(receipt: concat(head, "verdict=refused-member-list-unwritten\n"), outcome: exit_failure(reason: concat("workspace_source_pack: the member list could not be written: ", listed.error))) + let placeholder_listed = Filesystem.Write(path: placeholder_list_path, content: join(map(placeholders, p => concat(p.member as String, "\n")), "")) + if listed.success == false || placeholder_listed.success == false { + WorkspacePackRefused { refusal: PackRefusedMemberListUnwritten, why: join(["a member list could not be written: ", listed.error, placeholder_listed.error], "") } } else { - let pack_start = workspace_source_instant(label: "workspace-source-pack-start") - let packed = workspace_run_ok(run: run_shell_command_observe_outcome(command: tar_create_from_list_command(archive: archive, from_directory: "/", member_list: list_path), transport: LocalExec)) - let pack_span = workspace_source_span_words(start: pack_start, end_label: "workspace-source-pack-end") - match packed { - Present { value: why } => workspace_source_finish(receipt: concat(head, "verdict=refused-pack-failed\n"), outcome: exit_failure(reason: concat("workspace_source_pack: tar refused: ", why))) + match workspace_run_ok(run: run_shell_command_observe_outcome(command: tar_create_from_two_lists_command(archive: archive, first_directory: "/", first_list: list_path, second_directory: placeholder_root, second_list: placeholder_list_path), transport: LocalExec)) { + Present { value: why } => WorkspacePackRefused { refusal: PackRefusedTarFailed, why: concat("tar refused: ", why) } Absent => - match run_shell_command_observe_outcome(command: tar_list_command(archive: archive), transport: LocalExec) { - ProcessNotSpawned { program: _, cause: c } => workspace_source_finish(receipt: concat(head, "verdict=refused-readback-unrun\n"), outcome: exit_failure(reason: concat("workspace_source_pack: the archive could not be listed back: ", c as String))) - ProcessRan { observation: o } => - if o.exit_code != 0 { - workspace_source_finish(receipt: concat(head, "verdict=refused-readback-failed\n"), outcome: exit_failure(reason: concat("workspace_source_pack: tar -tf refused: ", o.stderr))) - } else { - match workspace_pack_readback(exclusions: workspace_exclusions(), archive_listing: o.stdout) { - WorkspacePackCarriesExcluded { member: m, by: x } => { - let removed = shell.Remove.RecursiveForce(path: staging.path) - workspace_source_finish(receipt: join([head, "verdict=refused-excluded-member member=", m as String, " rule=", x.spelling as String, " archive_removed=", if removed.success { "yes" } else { "no" }, "\n"], ""), outcome: exit_failure(reason: join(["workspace_source_pack: the archive carries ", m as String, ", which the credential exclusion ", x.spelling as String, " names; nothing was uploaded"], ""))) + match workspace_archive_listings(archive: archive) { + Absent => WorkspacePackRefused { refusal: PackRefusedReadbackFailed, why: "the archive could not be listed back" } + Present { value: listings } => { + let structural = workspace_pack_readback(exclusions: workspace_exclusions(), placeholders: placeholders, archive_listing: listings.names, size_listing: listings.sizes, archived_digests: fold(placeholders, init: empty_map(), f: (m, p) => map_insert(m: m, key: p.member as String, value: p.expected_digest.hex as String))) + match workspace_pack_readback_refusal(readback: structural) { + Present { value: _ } => WorkspacePackBuilt { archive: archive, readback: structural } + Absent => + match workspace_archived_placeholder_digests(staging: staging, archive: archive, placeholder_list: placeholder_list_path, placeholders: placeholders) { + Absent => WorkspacePackRefused { refusal: PackRefusedPlaceholderExtractFailed, why: "the placeholder members could not be extracted back out of the archive" } + Present { value: digests } => { + let r = workspace_pack_readback(exclusions: workspace_exclusions(), placeholders: placeholders, archive_listing: listings.names, size_listing: listings.sizes, archived_digests: digests) + let expected = length(members) + length(placeholders) + match r { + WorkspacePackClean { members: n } => + if n != expected { + WorkspacePackRefused { refusal: PackRefusedMemberCountMismatch, why: join(["the archive holds ", to_string(n), " members but the lists named ", to_string(expected)], "") } + } else { + WorkspacePackBuilt { archive: archive, readback: r } + } + WorkspacePackCarriesExcluded { member: _, by: _ } => WorkspacePackBuilt { archive: archive, readback: r } + WorkspacePackPlaceholderAbsent { member: _ } => WorkspacePackBuilt { archive: archive, readback: r } + WorkspacePackPlaceholderNotRegular { member: _, type_letter: _ } => WorkspacePackBuilt { archive: archive, readback: r } + WorkspacePackPlaceholderSizeDiffers { member: _, planned: _, archived: _ } => WorkspacePackBuilt { archive: archive, readback: r } + WorkspacePackPlaceholderUnread { member: _ } => WorkspacePackBuilt { archive: archive, readback: r } + WorkspacePackPlaceholderDigestDiffers { member: _, expected: _, archived: _ } => WorkspacePackBuilt { archive: archive, readback: r } } - WorkspacePackClean { members: n } => - if n != length(members) { - workspace_source_finish(receipt: concat(head, "verdict=refused-member-count-mismatch\n"), outcome: exit_failure(reason: join(["workspace_source_pack: the archive holds ", to_string(n), " members but the list named ", to_string(length(members))], ""))) - } else { - workspace_source_publish(head: concat(head, join(["archive_members=", to_string(n), " pack=", pack_span, "\n"], "")), archive: archive, staging: staging.path as String) - } } } + } + } } } } } } +// THE ARCHIVED PLACEHOLDER BYTES, HASHED: extract exactly the placeholder members into +// /readback and sha256sum each. A member that did not extract or could not be hashed is +// simply absent from the map, which the readback refuses as unread. An empty plan extracts nothing. +fn workspace_archived_placeholder_digests(staging: String, archive: String, placeholder_list: String, placeholders: List) -> Map? { + match placeholders { + Empty => Present { value: empty_map() } + Cons { head: _, tail: _ } => { + let into = join([staging, "/readback"], "") + let made = shell.Mkdir.Parents(path: into as FilePath) + if made.success == false { + none + } else { + match workspace_run_ok(run: run_shell_command_observe_outcome(command: tar_extract_listed_command(archive: archive, into_directory: into, member_list: placeholder_list), transport: LocalExec)) { + Present { value: _ } => none + Absent => Present { value: fold(placeholders, init: empty_map(), f: (m, p) => match workspace_file_digest(path: join([into, "/", p.member as String], "")) { + Present { value: d } => map_insert(m: m, key: p.member as String, value: d.hex as String) + Absent => m + }) } + } + } + } + } +} + +type WorkspaceReadbackRefusal { + refusal: WorkspacePackRefusal + why: String +} + +fn workspace_pack_clean_members(readback: WorkspacePackReadback) -> Int { + match readback { + WorkspacePackClean { members: n } => n + WorkspacePackCarriesExcluded { member: _, by: _ } => 0 + WorkspacePackPlaceholderAbsent { member: _ } => 0 + WorkspacePackPlaceholderNotRegular { member: _, type_letter: _ } => 0 + WorkspacePackPlaceholderSizeDiffers { member: _, planned: _, archived: _ } => 0 + WorkspacePackPlaceholderUnread { member: _ } => 0 + WorkspacePackPlaceholderDigestDiffers { member: _, expected: _, archived: _ } => 0 + } +} + +fn workspace_pack_readback_refusal(readback: WorkspacePackReadback) -> WorkspaceReadbackRefusal? { + match readback { + WorkspacePackClean { members: _ } => none + WorkspacePackCarriesExcluded { member: m, by: x } => + Present { value: WorkspaceReadbackRefusal { refusal: PackRefusedExcludedMember { member: m, rule: x.spelling as String }, why: join(["the archive carries ", m as String, ", which the workspace exclusion ", x.spelling as String, " names and no placeholder stands for"], "") } } + WorkspacePackPlaceholderAbsent { member: m } => + Present { value: WorkspaceReadbackRefusal { refusal: PackRefusedPlaceholderAbsent { member: m }, why: concat("the archive holds no member for the transcript placeholder ", m as String) } } + WorkspacePackPlaceholderNotRegular { member: m, type_letter: t } => + Present { value: WorkspaceReadbackRefusal { refusal: PackRefusedPlaceholderNotRegular { member: m, type_letter: t }, why: join(["the transcript placeholder ", m as String, " is archived as type '", t, "', not a regular file"], "") } } + WorkspacePackPlaceholderSizeDiffers { member: m, planned: p, archived: a } => + Present { value: WorkspaceReadbackRefusal { refusal: PackRefusedPlaceholderSize { member: m, planned: p, archived: a }, why: join(["the transcript placeholder ", m as String, " is archived at ", to_string(measure_count(m: a)), " bytes, not its planned ", to_string(measure_count(m: p))], "") } } + WorkspacePackPlaceholderUnread { member: m } => + Present { value: WorkspaceReadbackRefusal { refusal: PackRefusedPlaceholderUnread { member: m }, why: concat("the archived bytes of the transcript placeholder could not be extracted and hashed: ", m as String) } } + WorkspacePackPlaceholderDigestDiffers { member: m, expected: e, archived: a } => + Present { value: WorkspaceReadbackRefusal { refusal: PackRefusedPlaceholderDigest { member: m, expected: e.hex as String, archived: a }, why: join(["the archived bytes of ", m as String, " are not its staged placeholder (sha256 ", a, ", expected ", e.hex as String, "); the archive may carry real transcript content"], "") } } + } +} + +fn workspace_source_refuse_archive(head: String, staging: String, refusal: WorkspacePackRefusal, why: String) -> ProcessExit { + let removed = shell.Remove.RecursiveForce(path: staging as FilePath) + workspace_source_finish(receipt: join([head, "verdict=", workspace_pack_refusal_words(refusal: refusal), " archive_removed=", if removed.success { "yes" } else { "no" }, "\n"], ""), outcome: exit_failure(reason: join(["workspace_source_pack: ", why, "; nothing was uploaded"], ""))) +} + +type WorkspaceArchiveListings { + names: String + sizes: String +} + +fn workspace_archive_listings(archive: String) -> WorkspaceArchiveListings? { + match run_shell_command_observe_outcome(command: tar_list_command(archive: archive), transport: LocalExec) { + ProcessNotSpawned { program: _, cause: _ } => none + ProcessRan { observation: names } => + if names.exit_code != 0 { + none + } else { + match run_shell_command_observe_outcome(command: tar_list_sizes_command(archive: archive), transport: LocalExec) { + ProcessNotSpawned { program: _, cause: _ } => none + ProcessRan { observation: sizes } => if sizes.exit_code != 0 { none } else { Present { value: WorkspaceArchiveListings { names: names.stdout, sizes: sizes.stdout } } } + } + } + } +} + fn workspace_source_publish(head: String, archive: String, staging: String) -> ProcessExit { match read_staged_fabric_blob(path: archive as NonEmptyStr) { FabricBlobReadRefused { fault: f } => workspace_source_finish(receipt: concat(head, "verdict=refused-archive-unread\n"), outcome: exit_failure(reason: concat("workspace_source_pack: ", fabric_blob_fault_text(fault: f)))) diff --git a/dag/gunbc/fabric/workspace_transcript_rate.dag b/dag/gunbc/fabric/workspace_transcript_rate.dag new file mode 100644 index 00000000000..7a3d0da91df --- /dev/null +++ b/dag/gunbc/fabric/workspace_transcript_rate.dag @@ -0,0 +1,581 @@ +module gunbc.fabric.workspace_transcript_rate + +import std.types { String, NonEmptyStr, Int, Bool, List, Map } +import v2.std.collection { empty_map, map_insert, map_lookup } +import std.optional { Present, Absent } +import std.algebra { Cons, Empty } +import std.process { ProcessExit, ExitSuccess, exit_failure } +import std.measure { ByteSize, byte_size, measure_count, Nanosecond, nanosecond, nanosecond_count, Second, second, second_count, Bandwidth, bandwidth, bandwidth_count, bits_per_byte, ObservationInstant, ProcessMonotonic, WallClock, InstantOrder, InstantAfter, InstantIncomparable, InstantBefore, InstantSame } +import std.checkpointed_workspace { WorkspaceAgeOf, WorkspaceAgeUndefined, workspace_age_between } +import extdeps.filesystem.filesystem_io { Filesystem } +import extdeps.shell.exec { ProcessRunOutcome, ProcessRan, ProcessNotSpawned } +import extdeps.exec.command { ArgvCommand, LocalExec } +import extdeps.tools.findutils { find_regular_file_size_mtime_command } +import extdeps.tools.sleep { sleep_delay_seconds_second_carrier_projection } +import gunbc.command_runner { run_shell_command_observe_outcome } +import gunbc.floor_cost_distribution { permille_percentile } +import gunbc.hostname_read { hostname_short_read, HostnameShortCaptured, HostnameShortCaptureRefused } +import gunbc.host_effect { LocalShell } +import gunbc.fabric.workspace_source { + WorkspaceSourceRoot, workspace_source_fixed_roots, workspace_source_pack_host, + WorkspaceAgentStateSlot, AgentStateSlotAdmitted, AgentStateSlotAbsent, AgentStateSlotUnknown, + workspace_agent_state_slot_observe, workspace_agent_state_slot_words, workspace_root_member, + workspace_exclusions, workspace_path_exclusion, workspace_exclusion_spellings, workspace_transcript_exclusions, + CredentialExclusion, TranscriptExclusion, +} + +// HOW FAST THE OPERATOR'S AGENT TRANSCRIPTS ACTUALLY GROW, OBSERVED ON THE HOST THAT HOLDS THEM, FROM +// SIZES AND MODIFICATION TIMES ALONE (step 3 of the workspace stage-0 sequence, operator decision +// via warm-badger-442, 2026-10-05). The stage-0 pack (gunbc.fabric.workspace_source) carries every +// transcript as a same-size synthetic placeholder of a static tree, so its checkpoint-lag figures +// say nothing about a tree being written to; the 10 s / 60 s verdicts stay withheld until a churn +// phase appends to those placeholders at a realistic rate. This module produces that rate. On srv1 +// it samples, once per declared interval over a declared window, the byte size and mtime of every +// file the pack's own TranscriptExclusion rows match under the pinned agent-state roots, and +// records per interval what grew, what appeared, what shrank and how many bytes were appended. +// +// CONTENT IS NEVER READ. The only reader is extdeps.tools.findutils +// find_regular_file_size_mtime_command, whose -printf directives are answered from the inode and +// whose expression runs no other program; the witness checks the second fact on the command value. +// Transcript content is exactly what the operator ruled is not carried, and a rate instrument has no +// need of it. +// +// WHAT ONE RUN IS: one window, on one host, during whatever agent activity happened to be live. It +// is ONE SAMPLE OF THE WORKLOAD, NOT THE WORKLOAD, and every receipt says so (DESIGN 4d: the figure +// is a typed reading of one occasion, and a consumer that quotes it as the operator's rate is the +// defect). + +// ── WHAT IS OBSERVED, AND FOR HOW LONG ───────────────────────────────────────────────────────── + +// THE ROOTS ARE THE PACK'S OWN: the Claude agent-state root is the one the pack's slot admission +// (gunbc.fabric.workspace_source workspace_agent_state_slot_observe) admits from the same +// agent_state_slot input -- the active slot moves with the operator's credential swaps, so it is a +// declared input with no default, never a pinned path -- and the Codex home is the pack's fixed row, +// selected by its label. The gunbc clone is not observed: no transcript lives in it, and walking a +// build tree each interval would cost the window its resolution. +data workspace_transcript_rate_fixed_root_labels: List = ["codex-state"] + +// TEN MINUTES AT TEN SECONDS, declared and printed in every receipt. The interval matches the +// stage-0 checkpoint-lag target, so one row is one target-length of appends; the window spans +// sixty of them, which is the smallest population whose 95th percentile is not its maximum. +data workspace_transcript_rate_window: Second = second(count: 600) +data workspace_transcript_rate_sample_interval: Second = second(count: 10) + +data workspace_transcript_rate_receipt_path: String = "target/workspace-transcript-rate-receipt.txt" + +data workspace_transcript_rate_caveat: NonEmptyStr = "ONE SAMPLE OF WORKLOAD, NOT THE WORKLOAD: one window on one host during whatever agent activity was live; bytes_appended is a LOWER BOUND (stat sees net size change per interval, never writes undone or overwritten within it)" + +type TranscriptRateRootSelection + = TranscriptRateRootsSelected { roots: List } + | TranscriptRateRootUndeclared { label: NonEmptyStr } + | TranscriptRateSlotRefused { slot: WorkspaceAgentStateSlot } + +// The admitted slot's root first, then the fixed rows; a refused slot refuses the selection, carrying +// the admission so the receipt prints the pack's own words for it. +fn workspace_transcript_rate_roots(slot: WorkspaceAgentStateSlot, fixed: TranscriptRateRootSelection) -> TranscriptRateRootSelection { + match slot { + AgentStateSlotAbsent { observed: _ } => TranscriptRateSlotRefused { slot: slot } + AgentStateSlotUnknown { requested: _, observed: _ } => TranscriptRateSlotRefused { slot: slot } + AgentStateSlotAdmitted { slot: _, root: agent } => + match fixed { + TranscriptRateRootsSelected { roots: rs } => TranscriptRateRootsSelected { roots: Cons { head: agent, tail: rs } } + TranscriptRateRootUndeclared { label: l } => TranscriptRateRootUndeclared { label: l } + TranscriptRateSlotRefused { slot: s2 } => TranscriptRateSlotRefused { slot: s2 } + } + } +} + +fn workspace_transcript_rate_select_roots(labels: List, declared: List) -> TranscriptRateRootSelection { + match labels { + Empty => TranscriptRateRootsSelected { roots: [] } + Cons { head: l, tail: rest } => + match filter(declared, r => (r.label as String) == (l as String)) { + Empty => TranscriptRateRootUndeclared { label: l } + Cons { head: r, tail: _ } => + match workspace_transcript_rate_select_roots(labels: rest, declared: declared) { + TranscriptRateRootUndeclared { label: u } => TranscriptRateRootUndeclared { label: u } + TranscriptRateSlotRefused { slot: s } => TranscriptRateSlotRefused { slot: s } + TranscriptRateRootsSelected { roots: rs } => TranscriptRateRootsSelected { roots: Cons { head: r, tail: rs } } + } + } + } +} + +// ── ONE READING OF ONE ROOT: SIZE AND MTIME, NEVER CONTENT ───────────────────────────────────── + +// A FILE'S MODIFICATION TIME IS THE FILESYSTEM'S CLOCK, NOT THIS PROCESS'S: st_mtime counted from +// the epoch, as find's %T@ prints it. It has its own carrier so it can never stand where a sample +// instant (an ObservationInstant on this process's monotonic clock) is expected, and the two are +// never subtracted from each other; an mtime is only ever compared with another mtime. +type TranscriptFileMtime { + since_epoch: Nanosecond +} + +type TranscriptFileStat { + member: NonEmptyStr + size: ByteSize + mtime: TranscriptFileMtime +} + +// A ROOT THAT COULD NOT BE READ REFUSES, and so does a line that is not a record: a partial listing +// is not a smaller transcript population, it is a reading that did not happen, and an interval +// computed against it would report every unlisted file as removed. +type TranscriptRootReading + = TranscriptRootRead { files: List } + | TranscriptRootRefused { root: WorkspaceSourceRoot, detail: String } + +fn workspace_transcript_rate_root_command(root: WorkspaceSourceRoot) -> ArgvCommand { + find_regular_file_size_mtime_command(root: root.path as String) +} + +fn workspace_transcript_rate_root_reading(root: WorkspaceSourceRoot, run: ProcessRunOutcome) -> TranscriptRootReading { + match run { + ProcessNotSpawned { program: p, cause: c } => TranscriptRootRefused { root: root, detail: join([p as String, " did not run: ", c as String], "") } + ProcessRan { observation: o } => + if o.exit_code != 0 { + TranscriptRootRefused { root: root, detail: join(["find exited ", to_string(o.exit_code), " reading ", root.path as String, ": ", o.stderr], "") } + } else { + workspace_transcript_rate_lines(root: root, lines: filter(split(o.stdout, separator: "\n"), l => l != ""), files: []) + } + } +} + +// Each line is classified by the pack's own matcher over its member path, so this module restates +// no rule: a path the pack excludes as a transcript is observed, a credential-class match (counted +// first, as the pack counts it) or an unmatched path is not. Kept files accumulate in reverse and +// the order is irrelevant to every consumer, which keys them by member. +fn workspace_transcript_rate_lines(root: WorkspaceSourceRoot, lines: List, files: List) -> TranscriptRootReading { + match lines { + Empty => TranscriptRootRead { files: files } + Cons { head: l, tail: rest } => + match workspace_transcript_rate_line(root: root, line: l) { + TranscriptLineUnparsed { line: bad } => TranscriptRootRefused { root: root, detail: concat("find printed a line that is not a size, mtime and path record: ", bad) } + TranscriptLineOther => workspace_transcript_rate_lines(root: root, lines: rest, files: files) + TranscriptLineFile { stat: s } => workspace_transcript_rate_lines(root: root, lines: rest, files: Cons { head: s, tail: files }) + } + } +} + +type TranscriptLine + = TranscriptLineFile { stat: TranscriptFileStat } + | TranscriptLineOther + | TranscriptLineUnparsed { line: String } + +fn workspace_transcript_rate_line(root: WorkspaceSourceRoot, line: String) -> TranscriptLine { + match split(line, separator: "\t") { + Cons { head: size_text, tail: Cons { head: mtime_text, tail: Cons { head: first, tail: more } } } => { + let relative = join(Cons { head: first, tail: more }, "\t") + match parse_int(s: size_text) { + Absent => TranscriptLineUnparsed { line: line } + Present { value: n } => + match workspace_transcript_rate_mtime(text: mtime_text) { + Absent => TranscriptLineUnparsed { line: line } + Present { value: t } => + if n < 0 || relative == "" { + TranscriptLineUnparsed { line: line } + } else { + workspace_transcript_rate_classify(stat: TranscriptFileStat { member: workspace_root_member(root: root, relative: relative) as NonEmptyStr, size: byte_size(count: n), mtime: t }) + } + } + } + } + Cons { head: _, tail: Cons { head: _, tail: Empty } } => TranscriptLineUnparsed { line: line } + Cons { head: _, tail: Empty } => TranscriptLineUnparsed { line: line } + Empty => TranscriptLineUnparsed { line: line } + } +} + +fn workspace_transcript_rate_classify(stat: TranscriptFileStat) -> TranscriptLine { + match workspace_path_exclusion(exclusions: workspace_exclusions(), path: stat.member as String) { + Absent => TranscriptLineOther + Present { value: x } => + match x.class { + TranscriptExclusion => TranscriptLineFile { stat: stat } + CredentialExclusion => TranscriptLineOther + } + } +} + +// %T@ PRINTS SECONDS, A POINT AND A FRACTION (GNU find prints ten fractional digits; a filesystem +// with coarser timestamps prints zeros there). Nanoseconds are the first nine fractional digits, +// right-padded, so a shorter fraction reads as the value it spells rather than a smaller one. +fn workspace_transcript_rate_mtime(text: String) -> TranscriptFileMtime? { + match split(text, separator: ".") { + Cons { head: whole, tail: Empty } => + match parse_int(s: whole) { + Absent => none + Present { value: s } => if s < 0 { none } else { Present { value: TranscriptFileMtime { since_epoch: nanosecond(count: s * 1000000000) } } } + } + Cons { head: whole, tail: Cons { head: frac, tail: Empty } } => { + let padded = substring(s: concat(frac, "000000000"), start: 0, end: 9) + match parse_int(s: whole) { + Absent => none + Present { value: s } => + match parse_int(s: padded) { + Absent => none + Present { value: f } => if s < 0 || f < 0 || frac == "" { none } else { Present { value: TranscriptFileMtime { since_epoch: nanosecond(count: s * 1000000000 + f) } } } + } + } + } + Cons { head: _, tail: Cons { head: _, tail: Cons { head: _, tail: _ } } } => none + Empty => none + } +} + +// ── ONE SAMPLE: EVERY ROOT, AT ONE INSTANT ───────────────────────────────────────────────────── +// +// The instant is read on this process's monotonic clock immediately before the first root is +// walked, so an interval's span is the time between the starts of two walks; a walk's own duration +// is inside the span it opens, never subtracted from it. +type TranscriptRateSample + = TranscriptRateSampled { at: ObservationInstant, files: List } + | TranscriptRateSampleRefused { refusals: List } + +fn workspace_transcript_rate_sample_of(at: ObservationInstant, readings: List) -> TranscriptRateSample { + let refusals = flat_map(readings, r => match r { + TranscriptRootRefused { root: root, detail: d } => [join([root.label as String, " (", root.path as String, "): ", d], "")] + TranscriptRootRead { files: _ } => [] + }) + match refusals { + Cons { head: _, tail: _ } => TranscriptRateSampleRefused { refusals: refusals } + Empty => TranscriptRateSampled { at: at, files: flat_map(readings, r => match r { + TranscriptRootRead { files: fs } => fs + TranscriptRootRefused { root: _, detail: _ } => [] + }) } + } +} + +// ONE CLOCK DOMAIN FOR EVERY SAMPLE OF A WINDOW: this process's monotonic clock, wall basis -- the +// shape gunbc.fabric.workspace_source workspace_source_instant gives the pack's spans. +fn workspace_transcript_rate_instant(label: String) -> ObservationInstant { + ObservationInstant { domain: ProcessMonotonic { process: "workspace-transcript-rate" }, basis: WallClock, since_origin: nanosecond(count: observed_monotonic_nanos(label)) } +} + +fn workspace_transcript_rate_sample_wet(roots: List, label: String) -> TranscriptRateSample { + let at = workspace_transcript_rate_instant(label: label) + workspace_transcript_rate_sample_of(at: at, readings: map(roots, r => workspace_transcript_rate_root_reading(root: r, run: run_shell_command_observe_outcome(command: workspace_transcript_rate_root_command(root: r), transport: LocalExec)))) +} + +// ── ONE INTERVAL: THE DIFFERENCE OF TWO SAMPLES ──────────────────────────────────────────────── +// +// A FILE THAT SHRANK IS A TRUNCATION, REPORTED AS ONE, never a negative append: its delta is not +// subtracted from the bytes appended, because a rewrite that shortens a file appends nothing and +// would otherwise cancel real appends elsewhere in the interval. A CREATED file contributes its +// whole size, all of it written since the previous sample. A file whose mtime advanced at the same +// size was rewritten in place and appended nothing. The ACTIVE-WRITER COUNT is the number of +// distinct files written in the interval -- created, grown, shrunk or mtime-advanced. It counts +// FILES, not processes: one agent session appends to its own transcript and a subagent to its own, +// so it tracks live writers without being a process count, and stat cannot see the processes. +type TranscriptTruncation { + member: NonEmptyStr + from: ByteSize + to: ByteSize +} + +type TranscriptAppendInterval { + span: Nanosecond + files_grown: Int + files_created: Int + files_removed: Int + files_rewritten_same_size: Int + truncations: List + bytes_appended: ByteSize + active_writers: Int +} + +type TranscriptFileChange + = TranscriptFileCreated { size: ByteSize } + | TranscriptFileGrown { by: ByteSize } + | TranscriptFileShrank { truncation: TranscriptTruncation } + | TranscriptFileRewritten + | TranscriptFileUnchanged + +fn workspace_transcript_file_change(before: TranscriptFileStat?, after: TranscriptFileStat) -> TranscriptFileChange { + match before { + Absent => TranscriptFileCreated { size: after.size } + Present { value: b } => { + let delta = measure_count(m: after.size) - measure_count(m: b.size) + if delta > 0 { + TranscriptFileGrown { by: byte_size(count: delta) } + } else if delta < 0 { + TranscriptFileShrank { truncation: TranscriptTruncation { member: after.member, from: b.size, to: after.size } } + } else if nanosecond_count(n: after.mtime.since_epoch) != nanosecond_count(n: b.mtime.since_epoch) { + TranscriptFileRewritten + } else { + TranscriptFileUnchanged + } + } + } +} + +fn workspace_transcript_rate_index(files: List) -> Map { + fold(files, init: empty_map(), f: (m, s) => map_insert(m: m, key: s.member as String, value: s)) +} + +// Every file of the later sample is looked up once in the earlier one, so an interval costs one +// pass over each sample; a removed file is counted as the earlier files the later one did not keep. +fn workspace_transcript_rate_interval(before: List, after: List, span: Nanosecond) -> TranscriptAppendInterval { + let index = workspace_transcript_rate_index(files: before) + let changes = map(after, s => workspace_transcript_file_change(before: map_lookup(m: index, key: s.member as String), after: s)) + let created = length(filter(changes, c => match c { + TranscriptFileCreated { size: _ } => true + TranscriptFileGrown { by: _ } => false + TranscriptFileShrank { truncation: _ } => false + TranscriptFileRewritten => false + TranscriptFileUnchanged => false + })) + let grown = length(filter(changes, c => match c { + TranscriptFileCreated { size: _ } => false + TranscriptFileGrown { by: _ } => true + TranscriptFileShrank { truncation: _ } => false + TranscriptFileRewritten => false + TranscriptFileUnchanged => false + })) + let rewritten = length(filter(changes, c => match c { + TranscriptFileCreated { size: _ } => false + TranscriptFileGrown { by: _ } => false + TranscriptFileShrank { truncation: _ } => false + TranscriptFileRewritten => true + TranscriptFileUnchanged => false + })) + let truncations = flat_map(changes, c => match c { + TranscriptFileCreated { size: _ } => [] + TranscriptFileGrown { by: _ } => [] + TranscriptFileShrank { truncation: t } => [t] + TranscriptFileRewritten => [] + TranscriptFileUnchanged => [] + }) + let appended = fold(changes, init: 0, f: (acc, c) => match c { + TranscriptFileCreated { size: s } => acc + measure_count(m: s) + TranscriptFileGrown { by: d } => acc + measure_count(m: d) + TranscriptFileShrank { truncation: _ } => acc + TranscriptFileRewritten => acc + TranscriptFileUnchanged => acc + }) + TranscriptAppendInterval { + span: span, + files_grown: grown, + files_created: created, + files_removed: length(before) - (length(after) - created), + files_rewritten_same_size: rewritten, + truncations: truncations, + bytes_appended: byte_size(count: appended), + active_writers: created + grown + length(truncations) + rewritten, + } +} + +// ── THE OBSERVED RATE: THE CARRIER THE CHURN PHASE CONSUMES ──────────────────────────────────── +// +// THE RATE IS CARRIED AS ITS EXACT QUOTIENT PAIR -- bytes appended over a measured span, per +// interval -- because std.measure has no bytes-per-time type to carry it as one figure: its only +// rate of information is Bandwidth (DataRate, bits per second, a Nat count at One scale), which +// would floor every interval to whole bits per second and lose a quiet interval to zero. That gap +// is stated, not filled here: the receipt's rates are DERIVED from these pairs as Bandwidth (see +// workspace_transcript_rate_bandwidth), and a consumer derives what it needs from the pairs. +// +// THE CONSUMER IS A DECLARED FRONTIER (DESIGN 3c): the stage-0 churn phase (step 2 of the +// workspace stage-0 sequence, a later change under still-dove-673) appends to the synthetic +// transcript placeholders of gunbc.fabric.workspace_source at this observed rate, and the 10 s / +// 60 s checkpoint-lag verdicts are quotable only over a run that carries that churn. Its trigger is +// that change landing; until it does, the receipt renderer below is this carrier's executing +// consumer, and the receipt is the dispatch input the churn phase is parameterized from. +type TranscriptAppendRate { + host: NonEmptyStr + window: Second + interval: Second + rows: List + caveat: NonEmptyStr +} + +// A WINDOW EITHER COMPLETES OR REFUSES. A root that becomes unreadable mid-window, or a sleep that +// fails, ends the observation; the rows already taken are printed in the receipt beside the +// refusal, never summarized as if the window had finished. +type TranscriptRateObservation + = TranscriptRateObserved { rate: TranscriptAppendRate } + | TranscriptRateRefused { reason: String, rows: List } + | TranscriptRateClockUndefined { from: ObservationInstant, to: ObservationInstant, order: InstantOrder, rows: List } + +// THE SPAN IS DERIVED, NEVER SUBTRACTED: std.checkpointed_workspace workspace_age_between orders two +// consecutive sample instants first, so a later sample that does not follow the earlier one +// (InstantAfter) or one on another clock (InstantIncomparable) ends the window as its own typed +// refusal, TranscriptRateClockUndefined, instead of standing in a span. +// +// THE PURE WINDOW: a baseline sample and one sample per interval, each interval the difference of +// consecutive samples. The sampler and the pause are supplied, so the wet entry passes the real +// walk and the real sleep and a witness passes recorded samples; the fold itself is one function. +fn workspace_transcript_rate_window_of(baseline: TranscriptRateSample, remaining: Int, next: fn(Int) -> TranscriptRateSample, pause: fn(Int) -> Bool, rows: List) -> TranscriptRateObservation { + match baseline { + TranscriptRateSampleRefused { refusals: rs } => TranscriptRateRefused { reason: concat("a transcript root could not be read; no permission is widened here: ", join(rs, "; ")), rows: reverse(rows) } + TranscriptRateSampled { at: at, files: files } => + if remaining <= 0 { + TranscriptRateObserved { rate: TranscriptAppendRate { host: workspace_source_pack_host, window: workspace_transcript_rate_window, interval: workspace_transcript_rate_sample_interval, rows: reverse(rows), caveat: workspace_transcript_rate_caveat } } + } else if pause(remaining) == false { + TranscriptRateRefused { reason: "the pause between samples failed; the interval is not what the window declares", rows: reverse(rows) } + } else { + match next(remaining) { + TranscriptRateSampleRefused { refusals: rs } => TranscriptRateRefused { reason: concat("a transcript root became unreadable mid-window: ", join(rs, "; ")), rows: reverse(rows) } + TranscriptRateSampled { at: at2, files: files2 } => + match workspace_age_between(from: at, to: at2) { + WorkspaceAgeUndefined { order: o } => TranscriptRateClockUndefined { from: at, to: at2, order: o, rows: reverse(rows) } + WorkspaceAgeOf { age: span } => + workspace_transcript_rate_window_of( + baseline: TranscriptRateSampled { at: at2, files: files2 }, + remaining: remaining - 1, + next: next, + pause: pause, + rows: Cons { head: workspace_transcript_rate_interval(before: files, after: files2, span: span), tail: rows }, + ) + } + } + } + } +} + +fn workspace_transcript_rate_interval_count() -> Int { + second_count(s: workspace_transcript_rate_window) / second_count(s: workspace_transcript_rate_sample_interval) +} + +// ── THE SUMMARY, DERIVED FOR THE RECEIPT ─────────────────────────────────────────────────────── + +// A RATE IS A std.measure Bandwidth -- DataRate, bits per second -- derived from the exact quotient +// pair, never a bare number. Bits rather than bytes because Bandwidth is the one information-rate +// carrier std.measure has; eight bits to the octet (bits_per_byte) makes the conversion exact, and the +// floor is taken once, on bits. The receipt prints the carrier's own bits/s beside the bytes/s it +// implies (bits / 8, floored), which is the figure the churn phase is quoted in. +fn workspace_transcript_rate_bandwidth(bytes: ByteSize, span: Nanosecond) -> Bandwidth? { + let ns = nanosecond_count(n: span) + if ns <= 0 { none } else { Present { value: bandwidth(count: (measure_count(m: bytes) * bits_per_byte() * 1000000000) / ns) } } +} + +fn workspace_transcript_rate_row_rates(rows: List) -> List { + flat_map(rows, r => match workspace_transcript_rate_bandwidth(bytes: r.bytes_appended, span: r.span) { + Absent => [] + Present { value: v } => [v] + }) +} + +fn workspace_transcript_rate_percentile(rates: List, percent: Int) -> Bandwidth? { + match permille_percentile(values: map(rates, b => bandwidth_count(b: b)), percent: percent) { + Absent => none + Present { value: v } => Present { value: bandwidth(count: v) } + } +} + +fn workspace_transcript_rate_rate_words(name: String, rate: Bandwidth?, absent: String) -> String { + match rate { + Absent => join([" ", name, "_bits_per_s=", absent, " ", name, "_bytes_per_s=", absent], "") + Present { value: b } => join([" ", name, "_bits_per_s=", to_string(bandwidth_count(b: b)), " ", name, "_bytes_per_s=", to_string(bandwidth_count(b: b) / bits_per_byte())], "") + } +} + +fn workspace_transcript_rate_row_line(index: Int, r: TranscriptAppendInterval) -> String { + join([ + "interval ", to_string(index), + " span_ms=", to_string(nanosecond_count(n: r.span) / 1000000), + " bytes_appended=", to_string(measure_count(m: r.bytes_appended)), + " files_grown=", to_string(r.files_grown), + " files_created=", to_string(r.files_created), + " files_removed=", to_string(r.files_removed), + " files_rewritten_same_size=", to_string(r.files_rewritten_same_size), + " truncations=", to_string(length(r.truncations)), + join(map(r.truncations, t => join([" truncated=", t.member as String, ":", to_string(measure_count(m: t.from)), "->", to_string(measure_count(m: t.to))], "")), ""), + " active_writers=", to_string(r.active_writers), + ], "") +} + +fn workspace_transcript_rate_rows_text(rows: List, index: Int, acc: List) -> String { + match rows { + Empty => join(reverse(acc), "") + Cons { head: r, tail: rest } => workspace_transcript_rate_rows_text(rows: rest, index: index + 1, acc: Cons { head: concat(workspace_transcript_rate_row_line(index: index, r: r), "\n"), tail: acc }) + } +} + +fn workspace_transcript_rate_summary(rate: TranscriptAppendRate) -> String { + let total_bytes = byte_size(count: fold(rate.rows, init: 0, f: (acc, r) => acc + measure_count(m: r.bytes_appended))) + let total_span = nanosecond(count: fold(rate.rows, init: 0, f: (acc, r) => acc + nanosecond_count(n: r.span))) + let touched = fold(rate.rows, init: 0, f: (acc, r) => acc + r.active_writers) + let n = length(rate.rows) + let rates = workspace_transcript_rate_row_rates(rows: rate.rows) + join([ + "summary intervals=", to_string(n), + " measured_window_ms=", to_string(nanosecond_count(n: total_span) / 1000000), + " bytes_appended_total=", to_string(measure_count(m: total_bytes)), + workspace_transcript_rate_rate_words(name: "mean", rate: workspace_transcript_rate_bandwidth(bytes: total_bytes, span: total_span), absent: "unmeasured(no-span)"), + workspace_transcript_rate_rate_words(name: "p95", rate: workspace_transcript_rate_percentile(rates: rates, percent: 95), absent: "unmeasured(no-interval)"), + workspace_transcript_rate_rate_words(name: "max", rate: workspace_transcript_rate_percentile(rates: rates, percent: 100), absent: "unmeasured(no-interval)"), + " files_touched_per_interval_mean=", if n == 0 { "unmeasured(no interval)" } else { join([to_string(touched / n), " (", to_string(touched), "/", to_string(n), ")"], "") }, + " truncations_total=", to_string(fold(rate.rows, init: 0, f: (acc, r) => acc + length(r.truncations))), + "\n", + ], "") +} + +fn workspace_transcript_rate_receipt_head(roots: List) -> String { + join([ + "workspace_transcript_rate_observe host=", workspace_source_pack_host as String, "\n", + "window_s=", to_string(second_count(s: workspace_transcript_rate_window)), " interval_s=", to_string(second_count(s: workspace_transcript_rate_sample_interval)), "\n", + "observed=", workspace_exclusion_spellings(exclusions: workspace_transcript_exclusions), " (the pack's TranscriptExclusion rows; size and mtime only, content never read)\n", + join(map(roots, r => join(["root ", r.label as String, " path=", r.path as String], "")), "\n"), "\n", + workspace_transcript_rate_caveat as String, "\n", + ], "") +} + +fn workspace_transcript_rate_order_words(order: InstantOrder) -> String { + match order { + InstantAfter => "a later sample instant did not follow the earlier one on the same clock; no span is derived" + InstantIncomparable { left: _, right: _ } => "two sample instants are on different clock domains or bases; no span is derived" + InstantBefore => "the instants ordered, yet no span was derived" + InstantSame => "the instants were the same, yet no span was derived" + } +} + +fn workspace_transcript_rate_receipt(head: String, observation: TranscriptRateObservation) -> String { + match observation { + TranscriptRateObserved { rate: r } => join([head, workspace_transcript_rate_rows_text(rows: r.rows, index: 1, acc: []), workspace_transcript_rate_summary(rate: r), "verdict=observed\n"], "") + TranscriptRateRefused { reason: why, rows: rows } => join([head, workspace_transcript_rate_rows_text(rows: rows, index: 1, acc: []), "refused=", why, "\nverdict=refused\n"], "") + TranscriptRateClockUndefined { from: _, to: _, order: o, rows: rows } => join([head, workspace_transcript_rate_rows_text(rows: rows, index: 1, acc: []), "refused=", workspace_transcript_rate_order_words(order: o), "\nverdict=refused-clock\n"], "") + } +} + +// ── THE OBSERVATION, ON THE HOST THAT HOLDS THE TRANSCRIPTS ──────────────────────────────────── + +fn workspace_transcript_rate_finish(receipt: String, outcome: ProcessExit) -> ProcessExit { + let w = Filesystem.Write(path: workspace_transcript_rate_receipt_path, content: receipt) + if w.success == false { + exit_failure(reason: join(["workspace_transcript_rate_observe: receipt write failed: ", w.error, "\n", receipt], "")) + } else { + outcome + } +} + +fn workspace_transcript_rate_observe_wet() -> ProcessExit { + let host = match hostname_short_read(transport: LocalShell) { + HostnameShortCaptured { value: v } => v + HostnameShortCaptureRefused { reason: _ } => "" + } + if host != (workspace_source_pack_host as String) { + exit_failure(reason: join(["workspace_transcript_rate_observe: the transcripts live on ", workspace_source_pack_host as String, " but this job runs on '", host, "'; dispatch it with host=", workspace_source_pack_host as String], "")) + } else { + let slot = workspace_agent_state_slot_observe() + let slot_line = concat(workspace_agent_state_slot_words(admission: slot), "\n") + match workspace_transcript_rate_roots(slot: slot, fixed: workspace_transcript_rate_select_roots(labels: workspace_transcript_rate_fixed_root_labels, declared: workspace_source_fixed_roots)) { + TranscriptRateRootUndeclared { label: l } => exit_failure(reason: join(["workspace_transcript_rate_observe: no fixed workspace source root is labelled '", l as String, "'"], "")) + TranscriptRateSlotRefused { slot: _ } => workspace_transcript_rate_finish(receipt: join(["workspace_transcript_rate_observe host=", host, "\n", slot_line, "verdict=refused-agent-state-slot\n"], ""), outcome: exit_failure(reason: concat("workspace_transcript_rate_observe: ", slot_line))) + TranscriptRateRootsSelected { roots: roots } => { + let head = concat(workspace_transcript_rate_receipt_head(roots: roots), slot_line) + let observation = workspace_transcript_rate_window_of( + baseline: workspace_transcript_rate_sample_wet(roots: roots, label: "workspace-transcript-rate-baseline"), + remaining: workspace_transcript_rate_interval_count(), + next: k => workspace_transcript_rate_sample_wet(roots: roots, label: concat("workspace-transcript-rate-sample-", to_string(k))), + pause: _ => sleep_delay_seconds_second_carrier_projection(duration: workspace_transcript_rate_sample_interval), + rows: [], + ) + let receipt = workspace_transcript_rate_receipt(head: head, observation: observation) + match observation { + TranscriptRateObserved { rate: _ } => workspace_transcript_rate_finish(receipt: receipt, outcome: ExitSuccess) + TranscriptRateRefused { reason: why, rows: _ } => workspace_transcript_rate_finish(receipt: receipt, outcome: exit_failure(reason: concat("workspace_transcript_rate_observe: ", why))) + TranscriptRateClockUndefined { from: _, to: _, order: o, rows: _ } => workspace_transcript_rate_finish(receipt: receipt, outcome: exit_failure(reason: concat("workspace_transcript_rate_observe: ", workspace_transcript_rate_order_words(order: o)))) + } + } + } + } +} diff --git a/dag/gunbc/fleet/fleet_converge_workflow.dag b/dag/gunbc/fleet/fleet_converge_workflow.dag index a5898bdb942..386a3f8d787 100644 --- a/dag/gunbc/fleet/fleet_converge_workflow.dag +++ b/dag/gunbc/fleet/fleet_converge_workflow.dag @@ -48,7 +48,8 @@ import gunbc.machine_intake_mtcollins1_kvm_observer_observe { mtcollins1_kvm_obs import gunbc.machine_intake_megarac_kvm_still { megarac_kvm_observer_release_allowance } import gunbc.machine_intake_mtcollins1_ui_bundle_observe { mtcollins1_ui_bundle_artifact_glob, mtcollins1_ui_bundle_read_bound } import gunbc.fabric_writer_identity_observe { fabric_writer_identity_receipt_path } -import gunbc.fabric.workspace_source { workspace_source_receipt_path } +import gunbc.fabric.workspace_source { workspace_source_receipt_path, workspace_agent_state_slot_env } +import gunbc.fabric.workspace_transcript_rate { workspace_transcript_rate_receipt_path } import gunbc.fabric.workspace_checkpoint_instrument { workspace_measure_receipt_path, workspace_restore_receipt_path, workspace_source_object_env } import gunbc.machine_intake_mtcollins1_census_image_publish { mtcollins1_census_image_receipt_path } import gunbc.machine_intake_mtcollins1_census_member_readback { census_member_readback_receipt_path } @@ -179,6 +180,7 @@ import gunbc.ci_spec { gunbc_ci_workspace_source_pack_invoke, gunbc_ci_workspace_checkpoint_measure_invoke, gunbc_ci_workspace_checkpoint_restore_invoke, + gunbc_ci_workspace_transcript_rate_observe_invoke, gunbc_ci_approval_keyring_converge_invoke, gunbc_ci_approval_device_enrolment_code_issue_invoke, gunbc_ci_mtcollins1_boot_invoke, @@ -363,6 +365,7 @@ type FleetConvergeWorkflowMode | WorkspaceSourcePack | WorkspaceCheckpointMeasure | WorkspaceCheckpointRestore + | WorkspaceTranscriptRateObserve | ApprovalKeyringConverge | ApprovalDeviceEnrolmentCodeIssue | MtCollins1Boot @@ -442,6 +445,7 @@ fn fleet_converge_workflow_mode_wire(mode: FleetConvergeWorkflowMode) -> String WorkspaceSourcePack => "workspace_source_pack" WorkspaceCheckpointMeasure => "workspace_checkpoint_measure" WorkspaceCheckpointRestore => "workspace_checkpoint_restore" + WorkspaceTranscriptRateObserve => "workspace_transcript_rate_observe" ApprovalKeyringConverge => "approval_keyring_converge" ApprovalDeviceEnrolmentCodeIssue => "approval_device_enrolment_code_issue" MtCollins1Boot => "mtcollins1_boot" @@ -483,7 +487,7 @@ fn fleet_converge_spark_target_description() -> String { ], "") } -data fleet_converge_workflow_modes: List = [FullHostPlan, LaunchEnvironmentPlan, AllocationStorePlan, WorkspaceCommissioningPlan, FleetApply, OrgActionsObserve, AppControlPlaneObserve, MicrovmHostConverge, MicrovmNetworkObserve, MicrovmNetworkApply, GuestImageObserve, GuestImageConverge, MicrovmBootProbe, SparkGrants, SparkBootstrap, SparkServingApply, SparkNativeServingApply, SparkRuntimeImageProbe, SparkWirelessLinkConverge, SparkGrantsObserve, SparkV41CheckpointMaterialize, SparkV41RowStoreEncode, SparkV41RowStoreReadback, SparkV41EngramDifferential, SparkRuntimeImageProduce, SparkRuntimeImageDistribute, SparkArmGroupLoad, SparkArmCheckpointMaterialize, SparkArmGroupObserve, SparkArmGroupLaunchPlan, SparkArmGroupLaunch, SparkV41ServingLoad, DashboardDeploy, ApprovalBrokerDarkInstall, MicrovmControllerInstall, MicrovmSlotReserve, MicrovmSlotStart, RlmLaunchDeploymentReceipt, HostResetReturn, RunnerHostFileObserve, RunnerHostFileConverge, SitePxeEdgeObserve, SitePxeEdgeConverge, RunnerPasswordSessionToolConverge, RunnerBrowserToolchainConverge, R2MintPreflight, R2ObjectWriteMint, OrgRunnerRosterObserve, ApprovalKeyringConverge, ApprovalDeviceEnrolmentCodeIssue, MtCollins1Boot, MtCollins1FanObserve, MtCollins1UiBundleObserve, MtCollins1KvmObserverObserve, MtCollins1CensusImagePublish, MtCollins1CensusMemberReadback, MtCollins1CensusQemuHostObserve, MtCollins1CensusQemuToolchainConverge, HostCredentialCustodyConverge, AppKeyVersionVerify, R2BucketEnsure, R2BucketAdminMint, R2WorkspaceObjectReadMint, R2WorkspaceObjectWriteMint, R2CacheObjectReadMint, R2CacheObjectWriteMint, R2ConditionalPutRaceProbe, WorkspaceSourcePack, WorkspaceCheckpointMeasure, WorkspaceCheckpointRestore, FabricWriterIdentityObserve, PairServingD0, MicrovmRunnerGroupEnsure, GcpIamConverge, NamecheapObserve, PrinterWorkflow] +data fleet_converge_workflow_modes: List = [FullHostPlan, LaunchEnvironmentPlan, AllocationStorePlan, WorkspaceCommissioningPlan, FleetApply, OrgActionsObserve, AppControlPlaneObserve, MicrovmHostConverge, MicrovmNetworkObserve, MicrovmNetworkApply, GuestImageObserve, GuestImageConverge, MicrovmBootProbe, SparkGrants, SparkBootstrap, SparkServingApply, SparkNativeServingApply, SparkRuntimeImageProbe, SparkWirelessLinkConverge, SparkGrantsObserve, SparkV41CheckpointMaterialize, SparkV41RowStoreEncode, SparkV41RowStoreReadback, SparkV41EngramDifferential, SparkRuntimeImageProduce, SparkRuntimeImageDistribute, SparkArmGroupLoad, SparkArmCheckpointMaterialize, SparkArmGroupObserve, SparkArmGroupLaunchPlan, SparkArmGroupLaunch, SparkV41ServingLoad, DashboardDeploy, ApprovalBrokerDarkInstall, MicrovmControllerInstall, MicrovmSlotReserve, MicrovmSlotStart, RlmLaunchDeploymentReceipt, HostResetReturn, RunnerHostFileObserve, RunnerHostFileConverge, SitePxeEdgeObserve, SitePxeEdgeConverge, RunnerPasswordSessionToolConverge, RunnerBrowserToolchainConverge, R2MintPreflight, R2ObjectWriteMint, OrgRunnerRosterObserve, ApprovalKeyringConverge, ApprovalDeviceEnrolmentCodeIssue, MtCollins1Boot, MtCollins1FanObserve, MtCollins1UiBundleObserve, MtCollins1KvmObserverObserve, MtCollins1CensusImagePublish, MtCollins1CensusMemberReadback, MtCollins1CensusQemuHostObserve, MtCollins1CensusQemuToolchainConverge, HostCredentialCustodyConverge, AppKeyVersionVerify, R2BucketEnsure, R2BucketAdminMint, R2WorkspaceObjectReadMint, R2WorkspaceObjectWriteMint, R2CacheObjectReadMint, R2CacheObjectWriteMint, R2ConditionalPutRaceProbe, WorkspaceSourcePack, WorkspaceCheckpointMeasure, WorkspaceCheckpointRestore, WorkspaceTranscriptRateObserve, FabricWriterIdentityObserve, PairServingD0, MicrovmRunnerGroupEnsure, GcpIamConverge, NamecheapObserve, PrinterWorkflow] // WHICH RELEASE PRODUCT A MODE'S JOB RUNS, NAMED PER MODE WITH NO WILDCARD (as the scope and fleet-key // matches above name theirs). A mode whose steps run only gunbc and claim_executor declares the // compiler pair (gunbc.fleet_release_bins_key compiler_pair_release_product): its build job then @@ -549,6 +553,7 @@ fn fleet_converge_mode_release_product(mode: FleetConvergeWorkflowMode) -> Fleet WorkspaceSourcePack => CompilerPairOnly WorkspaceCheckpointMeasure => CompilerPairOnly WorkspaceCheckpointRestore => CompilerPairOnly + WorkspaceTranscriptRateObserve => CompilerPairOnly OrgRunnerRosterObserve => SixteenBinaryPack ApprovalKeyringConverge => SixteenBinaryPack ApprovalDeviceEnrolmentCodeIssue => CompilerPairOnly @@ -664,6 +669,7 @@ fn fleet_converge_mode_scope(mode: FleetConvergeWorkflowMode) -> FleetConvergeSc WorkspaceSourcePack => none WorkspaceCheckpointMeasure => none WorkspaceCheckpointRestore => none + WorkspaceTranscriptRateObserve => none ApprovalKeyringConverge => none ApprovalDeviceEnrolmentCodeIssue => none MtCollins1Boot => none @@ -768,6 +774,7 @@ fn fleet_converge_mode_fleet_ssh_key_demand(mode: FleetConvergeWorkflowMode) -> WorkspaceSourcePack => FleetSshKeyNotConsumed WorkspaceCheckpointMeasure => FleetSshKeyNotConsumed WorkspaceCheckpointRestore => FleetSshKeyNotConsumed + WorkspaceTranscriptRateObserve => FleetSshKeyNotConsumed FullHostPlan => FleetSshKeyConsumed LaunchEnvironmentPlan => FleetSshKeyConsumed AllocationStorePlan => FleetSshKeyConsumed @@ -1490,6 +1497,7 @@ fn fleet_converge_mode_mutation_domain(mode: FleetConvergeWorkflowMode) -> Fleet WorkspaceSourcePack => ExecutorDomain WorkspaceCheckpointMeasure => ExecutorDomain WorkspaceCheckpointRestore => ExecutorDomain + WorkspaceTranscriptRateObserve => ExecutorDomain ApprovalKeyringConverge => ExecutorDomain ApprovalDeviceEnrolmentCodeIssue => SubjectDomain { group: fleet_subject_mutation_group_approval_device_store } MtCollins1Boot => ExecutorDomain @@ -2522,6 +2530,7 @@ data fleet_converge_fabric_writer_identity_observe_receipt_if: String = join(["a data fleet_converge_workspace_source_pack_step_if: String = fleet_converge_mode_step_if(mode: WorkspaceSourcePack) data fleet_converge_workspace_checkpoint_measure_step_if: String = fleet_converge_mode_step_if(mode: WorkspaceCheckpointMeasure) data fleet_converge_workspace_checkpoint_restore_step_if: String = fleet_converge_mode_step_if(mode: WorkspaceCheckpointRestore) +data fleet_converge_workspace_transcript_rate_observe_step_if: String = fleet_converge_mode_step_if(mode: WorkspaceTranscriptRateObserve) // THE INSTRUMENT STEPS' ENVELOPE, STATED AS AN ENVELOPE, NOT A MEASUREMENT: the time these steps take // IS what stage 0 measures (a twenty-thousand-file workspace hashed and put one chunk at a time), so @@ -2544,7 +2553,9 @@ fn fleet_converge_workspace_instrument_step(name: String, id: String, run: Strin } fn fleet_converge_workspace_source_pack_step() -> Step { - fleet_converge_workspace_instrument_step(name: "Workspace source pack: the operator workspace minus credentials and build output, into the workspace bucket by its SHA-256", id: "workspace_source_pack", run: gunbc_ci_workspace_source_pack_invoke(), if_condition: fleet_converge_workspace_source_pack_step_if, extra_env: []) + fleet_converge_workspace_instrument_step(name: "Workspace source pack: the operator workspace minus credentials and build output, into the workspace bucket by its SHA-256", id: "workspace_source_pack", run: gunbc_ci_workspace_source_pack_invoke(), if_condition: fleet_converge_workspace_source_pack_step_if, extra_env: [ + kv(key: workspace_agent_state_slot_env as String, value: yaml_string(s: "${{ github.event.inputs.agent_state_slot }}")), + ]) } fn fleet_converge_workspace_source_pack_receipt_upload_step() -> Step { @@ -2587,6 +2598,37 @@ fn fleet_converge_workspace_checkpoint_restore_receipt_upload_step() -> Step { ) } +// THE TRANSCRIPT APPEND-RATE OBSERVATION (gunbc.fabric.workspace_transcript_rate) reads sizes and +// mtimes on srv1 and touches no bucket, so it carries no WIF token: it is the instrument envelope +// without the credential. Its timeout is an ENVELOPE around a declared ten-minute window plus one +// walk of the agent-state roots per interval, three times the window so a slow walk still reaches +// its receipt; the window, not this bound, is what the receipt reports. +data fleet_converge_workspace_transcript_rate_step_timeout_minutes: Duration = 30 + +fn fleet_converge_workspace_transcript_rate_observe_step() -> Step { + RunStep { + name: Present { value: "Workspace transcript append rate: size and mtime of every transcript file on srv1, sampled per interval over a declared window, content never read" }, + id: Present { value: "workspace_transcript_rate_observe" }, + run: gunbc_ci_workspace_transcript_rate_observe_invoke(), + shell: none, + env: Present { value: [kv(key: workspace_agent_state_slot_env as String, value: yaml_string(s: "${{ github.event.inputs.agent_state_slot }}"))] }, + working_directory: none, + if_condition: Present { value: fleet_converge_workspace_transcript_rate_observe_step_if }, + continue_on_error: none, + timeout_minutes: Present { value: fleet_converge_workspace_transcript_rate_step_timeout_minutes } + } +} + +fn fleet_converge_workspace_transcript_rate_observe_receipt_upload_step() -> Step { + fleet_converge_receipt_upload_step( + id: "workspace_transcript_rate_observe_receipt_upload", + name: "Upload workspace transcript append-rate receipt", + artifact: "workspace-transcript-rate-observe", + path: workspace_transcript_rate_receipt_path, + if_condition: join(["always() && ", fleet_converge_workspace_transcript_rate_observe_step_if], "") + ) +} + fn fleet_converge_fabric_writer_identity_observe_step() -> Step { RunStep { name: Present { value: "Fabric writer identity: the login this host presents at the fabric DB's served door (reads only)" }, @@ -4502,6 +4544,8 @@ fn fleet_converge_job() -> Job { fleet_converge_workspace_checkpoint_measure_receipt_upload_step(), fleet_converge_workspace_checkpoint_restore_step(), fleet_converge_workspace_checkpoint_restore_receipt_upload_step(), + fleet_converge_workspace_transcript_rate_observe_step(), + fleet_converge_workspace_transcript_rate_observe_receipt_upload_step(), fleet_converge_approval_keyring_converge_step(), fleet_converge_approval_device_enrolment_code_issue_step(), fleet_converge_microvm_runner_group_ensure_step(), @@ -4764,6 +4808,7 @@ fn fleet_converge_mode_job_id(mode: FleetConvergeWorkflowMode) -> NonEmptyStr { WorkspaceSourcePack => fleet_converge_shared_job_id WorkspaceCheckpointMeasure => fleet_converge_shared_job_id WorkspaceCheckpointRestore => fleet_converge_shared_job_id + WorkspaceTranscriptRateObserve => fleet_converge_shared_job_id FabricWriterIdentityObserve => fleet_converge_shared_job_id PairServingD0 => fleet_converge_shared_job_id MicrovmRunnerGroupEnsure => fleet_converge_shared_job_id @@ -4985,7 +5030,7 @@ data fleet_converge_dispatch_inputs: List = [ }, DispatchInput { name: "mode", - description: Present { value: "plan shows membership hunks; apply executes a prior plan artifact; org_actions_observe validates the org credential and refuses on runner-group drift; org_runner_roster_observe reads the organization's self-hosted runner registrations to exhaustion under the same token and refuses unless the roster is complete; app_control_plane_observe reads the gunbai-ci App registration and webhook config with a short-lived App JWT and refuses on hook-config drift; app_key_version_verify reads the gunbai-ci App private key at the EXACT Secret Manager version named by app_key_version, mints an installation token with it, and refuses unless GitHub accepts it and the key's rotation deadline has not passed -- no add, disable or destroy; runner_browser_toolchain_converge installs the declared Playwright/Chromium toolchain (apt host libraries as the administrator, digest-pinned node, Playwright and Chromium archives into the job user's root) on the selected host, which must be in the pool that runs the floor job, and refuses unless every digest, version and host library reads back and headless Chromium renders a local page; microvm_host_converge installs the cited Firecracker release on the selected host and reads the kvm grant back, refusing by name when the grant has not landed (the grant itself is applied by the full-host apply spine); microvm_network_apply stages the slot and host network files the model renders at the named expected_revision as root:root 0600 in a root-only directory over the fleet SSH edge as the host's ADMINISTRATOR, installs them with the modeled operations, reloads networkd, systemd-sysctl and the nft loader unit, and reads the ruleset back -- the job user is granted none of it, because install plus systemctl over content that principal can write is arbitrary root for any pull request; guest_image_observe reports the micro-VM base artifacts and their measured digests on the selected host and builds nothing; guest_image_converge builds the runner guest image there; microvm_boot_probe boots that image and reads its serial console; spark_grants and spark_bootstrap prepare the selected Spark; spark_serving_apply applies the promoted fabric groups' pair vLLM units over the password session, workers before heads; spark_native_serving_apply relaunches group B's native four-rank arm as ONE BOUNDED TRANSACTION -- an all-host preflight that mutates nothing and refuses on any unresolved artifact from an earlier run, the incumbent UNIT preserved (plus a diagnostic inspect of the incumbent container -- the container itself cannot outlive its unit, because these run --rm in the foreground under systemd), the head applied BEFORE the workers so every rank joins the head's new rendezvous store, then a readback of the COMPLETE realization through each container's own inspect -- every field the create spec states, the effective --node-rank off that container's argv, and NCCL's typed transport verdict on that incarnation -- plus the head's front door answering with this arm's served model, and then commit or a rollback of the whole arm -- and it is a separate mode because it reloads a 328 GB arm and nobody should reach it by asking for the pair units; spark_runtime_image_probe pulls the pinned runtime image on the selected Spark and reads its capabilities from inside its own digest, changing nothing; spark_wireless_link_converge reads the selected Spark's Wi-Fi power save (runtime via iw, persisted via the NetworkManager profile), link and kernel disconnect count, sets whichever realization differs from the declared intent, and refuses unless the readback decides Noop; spark_grants_observe reads every procured Spark's sudo grant listing as gunbc-automation and the bootstrap principal, and its sudoers drop-in shape, and writes nothing; spark_v41_checkpoint_materialize fetches the admitted published DeepSeek V4.1 files onto the selected Group A Spark (about 510 GB; spark_v41_row_store_encode encodes the eight Engram row stores from the verified shards on the selected Group A Spark and reads each store's sha256; spark_v41_row_store_readback reads those stores back at their header, first and last record and every rank seam, with the published source rows at the same rows, and writes nothing; spark_v41_engram_differential compares upstream's Engram lookup kernel with the design-B file-backed lookup over sampled real rows of every row store, byte for byte, and writes nothing; it states the requirement and refuses before fetching when the disk cannot hold it), publishes each only after its sha256 matches the manifest, leaves a present file with the right digest alone and refuses one with the wrong digest, and reads the storage-backed Engram spans from the verified shards; a transfer runs detached and a rerun reattaches; spark_v41_runtime_image_build PRODUCES the DeepSeek V4.1 image on the selected Spark -- it verifies the candidate's three FlashInfer wheels against the digests the candidate keys, converges the patched source tree, builds from it, reads the produced configuration digest back from inside the image through the probe route, and admits that digest against the candidate's own recipe, refusing a digest that does not recompute from it -- and it is a separate mode from the probe because it occupies one host for hours where the probe occupies it for minutes; spark_v41_runtime_image_distribute moves that produced image, named by the configuration digest its production receipt read back, from the host that receipt names to the selected Group A Spark -- save into its fabric blob root, pulled by the target straight over the fabric rail, load -- after stating its size against every filesystem a copy lands on, leaves a target already holding the digest untouched, refuses a target holding a different image under the tag, and refuses unless the target's image inspect Id reads back as that digest; spark_v41_group_a_launch reads the production image back under its tag on every Group A host, reads its registry inside its digest and every host's occupancy, and only when Group A is suspended for this candidate with every host held and vacant stages the Engram manifest and applies the V4.1 four-rank arm as one transaction at the capacity measurement's shape -- the target names only the session host; spark_v41_serving_load runs the shared serving-load runner against the V4.1 head (target must be srv6): one vllm bench serve step per capacity-measurement concurrency, metrics scraped around each, host pressure read on every Group A rank, and the staircase stop rules applied over the worst rank; it changes no unit and writes only its receipt; host_reset_return drives a rostered reset subject through its controller and measures the return FROM A PEER, and takes reset_observer rather than host because the observer must not be the subject; runner_host_file_observe reads the runner teardown drop-in, the needrestart deferral and the loaded teardown on the selected host as its administrator and writes nothing; runner_host_file_converge writes whichever of those two files differs, reloads systemd only if the drop-in changed, and refuses unless the manager then reports the declared teardown; microvm_controller_install writes the microVM slot controller's root-owned release locus (gunbc + sources + Firecracker + jailer) and the gunbc-microvm-slot@ template unit and the gunbc-microvm-slot-reserve broker unit on srv1 and starts neither; microvm_slot_reserve starts that broker unit once on srv1, which reserves the shakedown cell through the fabric broker route from inside the root process (slot, demand and offer derived from the model, never inputs), and uploads that invocation's reservation receipt, failing unless the reservation committed; microvm_slot_start starts the shakedown slot's controller unit once on srv1 (the instance is derived from the model, never an input), waits for it bounded by the unit's own stop timeout, and uploads that invocation's controller receipt; microvm_runner_group_ensure (srv1 only) reads the organization's runner groups and, only when the microvm-shakedown group is absent, files ONE operator approval, creates it restricted to the shakedown workflow on the default branch, and refuses unless the readback holds that restriction; mtcollins1_census_qemu_host_observe reads the selected host's KVM device and, under the job user's census QEMU root, the qemu-system-aarch64 build, its ldd libraries and the AAVMF images against their pins, and writes nothing; mtcollins1_census_qemu_toolchain_converge places that root as the job user (digest-pinned noble .debs unpacked with dpkg-deb -x, no apt, no Recommends) and refuses unless the same observe reads it ready; workspace_source_pack (host=srv1) enumerates the operator workspace as the job user, drops every path the credential exclusion row names and every build output, tars exactly the remainder, reads the archive back and refuses if any member is excluded, and puts it into the workspace bucket under its SHA-256, refusing by name when the runner cannot read a source root; workspace_checkpoint_measure (host=srv3, workspace_source_object = that SHA-256) refuses unless the job user's home is on btrfs, then times a read-only subvolume snapshot, a pinned kopia scan of it, the content-addressed chunk upload and the gated revision commit, and receipts every time with the 10 s / 60 s / 10 GB draft verdicts; workspace_checkpoint_restore (host=srv3) commits a small authored workspace, puts one chunk with no head advance, destroys the directory, restores it from the head closure and requires byte-equality with the uncommitted chunk absent -- both srv3 modes refuse their commit while the R2 head's CAS ground is uncited" }, + description: Present { value: "plan shows membership hunks; apply executes a prior plan artifact; org_actions_observe validates the org credential and refuses on runner-group drift; org_runner_roster_observe reads the organization's self-hosted runner registrations to exhaustion under the same token and refuses unless the roster is complete; app_control_plane_observe reads the gunbai-ci App registration and webhook config with a short-lived App JWT and refuses on hook-config drift; app_key_version_verify reads the gunbai-ci App private key at the EXACT Secret Manager version named by app_key_version, mints an installation token with it, and refuses unless GitHub accepts it and the key's rotation deadline has not passed -- no add, disable or destroy; runner_browser_toolchain_converge installs the declared Playwright/Chromium toolchain (apt host libraries as the administrator, digest-pinned node, Playwright and Chromium archives into the job user's root) on the selected host, which must be in the pool that runs the floor job, and refuses unless every digest, version and host library reads back and headless Chromium renders a local page; microvm_host_converge installs the cited Firecracker release on the selected host and reads the kvm grant back, refusing by name when the grant has not landed (the grant itself is applied by the full-host apply spine); microvm_network_apply stages the slot and host network files the model renders at the named expected_revision as root:root 0600 in a root-only directory over the fleet SSH edge as the host's ADMINISTRATOR, installs them with the modeled operations, reloads networkd, systemd-sysctl and the nft loader unit, and reads the ruleset back -- the job user is granted none of it, because install plus systemctl over content that principal can write is arbitrary root for any pull request; guest_image_observe reports the micro-VM base artifacts and their measured digests on the selected host and builds nothing; guest_image_converge builds the runner guest image there; microvm_boot_probe boots that image and reads its serial console; spark_grants and spark_bootstrap prepare the selected Spark; spark_serving_apply applies the promoted fabric groups' pair vLLM units over the password session, workers before heads; spark_native_serving_apply relaunches group B's native four-rank arm as ONE BOUNDED TRANSACTION -- an all-host preflight that mutates nothing and refuses on any unresolved artifact from an earlier run, the incumbent UNIT preserved (plus a diagnostic inspect of the incumbent container -- the container itself cannot outlive its unit, because these run --rm in the foreground under systemd), the head applied BEFORE the workers so every rank joins the head's new rendezvous store, then a readback of the COMPLETE realization through each container's own inspect -- every field the create spec states, the effective --node-rank off that container's argv, and NCCL's typed transport verdict on that incarnation -- plus the head's front door answering with this arm's served model, and then commit or a rollback of the whole arm -- and it is a separate mode because it reloads a 328 GB arm and nobody should reach it by asking for the pair units; spark_runtime_image_probe pulls the pinned runtime image on the selected Spark and reads its capabilities from inside its own digest, changing nothing; spark_wireless_link_converge reads the selected Spark's Wi-Fi power save (runtime via iw, persisted via the NetworkManager profile), link and kernel disconnect count, sets whichever realization differs from the declared intent, and refuses unless the readback decides Noop; spark_grants_observe reads every procured Spark's sudo grant listing as gunbc-automation and the bootstrap principal, and its sudoers drop-in shape, and writes nothing; spark_v41_checkpoint_materialize fetches the admitted published DeepSeek V4.1 files onto the selected Group A Spark (about 510 GB; spark_v41_row_store_encode encodes the eight Engram row stores from the verified shards on the selected Group A Spark and reads each store's sha256; spark_v41_row_store_readback reads those stores back at their header, first and last record and every rank seam, with the published source rows at the same rows, and writes nothing; spark_v41_engram_differential compares upstream's Engram lookup kernel with the design-B file-backed lookup over sampled real rows of every row store, byte for byte, and writes nothing; it states the requirement and refuses before fetching when the disk cannot hold it), publishes each only after its sha256 matches the manifest, leaves a present file with the right digest alone and refuses one with the wrong digest, and reads the storage-backed Engram spans from the verified shards; a transfer runs detached and a rerun reattaches; spark_v41_runtime_image_build PRODUCES the DeepSeek V4.1 image on the selected Spark -- it verifies the candidate's three FlashInfer wheels against the digests the candidate keys, converges the patched source tree, builds from it, reads the produced configuration digest back from inside the image through the probe route, and admits that digest against the candidate's own recipe, refusing a digest that does not recompute from it -- and it is a separate mode from the probe because it occupies one host for hours where the probe occupies it for minutes; spark_v41_runtime_image_distribute moves that produced image, named by the configuration digest its production receipt read back, from the host that receipt names to the selected Group A Spark -- save into its fabric blob root, pulled by the target straight over the fabric rail, load -- after stating its size against every filesystem a copy lands on, leaves a target already holding the digest untouched, refuses a target holding a different image under the tag, and refuses unless the target's image inspect Id reads back as that digest; spark_v41_group_a_launch reads the production image back under its tag on every Group A host, reads its registry inside its digest and every host's occupancy, and only when Group A is suspended for this candidate with every host held and vacant stages the Engram manifest and applies the V4.1 four-rank arm as one transaction at the capacity measurement's shape -- the target names only the session host; spark_v41_serving_load runs the shared serving-load runner against the V4.1 head (target must be srv6): one vllm bench serve step per capacity-measurement concurrency, metrics scraped around each, host pressure read on every Group A rank, and the staircase stop rules applied over the worst rank; it changes no unit and writes only its receipt; host_reset_return drives a rostered reset subject through its controller and measures the return FROM A PEER, and takes reset_observer rather than host because the observer must not be the subject; runner_host_file_observe reads the runner teardown drop-in, the needrestart deferral and the loaded teardown on the selected host as its administrator and writes nothing; runner_host_file_converge writes whichever of those two files differs, reloads systemd only if the drop-in changed, and refuses unless the manager then reports the declared teardown; microvm_controller_install writes the microVM slot controller's root-owned release locus (gunbc + sources + Firecracker + jailer) and the gunbc-microvm-slot@ template unit and the gunbc-microvm-slot-reserve broker unit on srv1 and starts neither; microvm_slot_reserve starts that broker unit once on srv1, which reserves the shakedown cell through the fabric broker route from inside the root process (slot, demand and offer derived from the model, never inputs), and uploads that invocation's reservation receipt, failing unless the reservation committed; microvm_slot_start starts the shakedown slot's controller unit once on srv1 (the instance is derived from the model, never an input), waits for it bounded by the unit's own stop timeout, and uploads that invocation's controller receipt; microvm_runner_group_ensure (srv1 only) reads the organization's runner groups and, only when the microvm-shakedown group is absent, files ONE operator approval, creates it restricted to the shakedown workflow on the default branch, and refuses unless the readback holds that restriction; mtcollins1_census_qemu_host_observe reads the selected host's KVM device and, under the job user's census QEMU root, the qemu-system-aarch64 build, its ldd libraries and the AAVMF images against their pins, and writes nothing; mtcollins1_census_qemu_toolchain_converge places that root as the job user (digest-pinned noble .debs unpacked with dpkg-deb -x, no apt, no Recommends) and refuses unless the same observe reads it ready; workspace_source_pack (host=srv1, agent_state_slot = an existing .claude@, no default) enumerates the operator workspace as the job user, drops every path the credential exclusion row names and every build output, stands same-size seeded placeholders in for transcript files, tars the remainder and the placeholders, reads the archive back and refuses if any credential is carried or any placeholder's archived bytes are not its staged digest, and puts it into the workspace bucket under its SHA-256, refusing by name when the runner cannot read a source root; workspace_checkpoint_measure (host=srv3, workspace_source_object = that SHA-256) refuses unless the job user's home is on btrfs, then times a read-only subvolume snapshot, a pinned kopia scan of it, the content-addressed chunk upload and the gated revision commit, and receipts every time, withholding the 10 s / 60 s draft verdicts while churn is unmeasured and deciding the 10 GB quota over the static size; workspace_checkpoint_restore (host=srv3) commits a small authored workspace, puts one chunk with no head advance, destroys the directory, restores it from the head closure and requires byte-equality with the uncommitted chunk absent -- both srv3 modes refuse their commit while the R2 head's CAS ground is uncited; workspace_transcript_rate_observe (host=srv1, agent_state_slot as for workspace_source_pack, admitted by the same rule) samples the size and mtime of every transcript file the pack's transcript exclusion rows match under that slot and the Codex home, once per interval over a ten-minute window, never opening content, and receipts per-interval growth, creations, truncations and active writers with mean and p95 bytes/s as ONE sample of workload" }, required: true, default: none, type: InputChoice { options: fleet_converge_mode_options }, @@ -5115,6 +5160,13 @@ data fleet_converge_dispatch_inputs: List = [ DispatchInput { name: "printer_project_sha256", description: Present { value: "SHA-256 of the reviewed sliced project" }, required: false, default: none, type: InputString }, DispatchInput { name: "printer_credential_version", description: Present { value: "Numeric Secret Manager version returned by observation" }, required: false, default: none, type: InputString }, DispatchInput { name: "printer_bed_clear", description: Present { value: "Operator reports this printer bed is clear for this dispatch" }, required: false, default: none, type: InputChoice { options: ["false", "true"] } }, + DispatchInput { + name: "agent_state_slot", + description: Present { value: "workspace_source_pack and workspace_transcript_rate_observe: the Claude auth slot whose agent state is packed, as in /home/briansrls/.claude@ on srv1; admitted only if that directory exists, refused naming the observed slots otherwise; no default" }, + required: false, + default: none, + type: InputString, + }, DispatchInput { name: "workspace_source_object", description: Present { value: "workspace_checkpoint_measure only: the lower-hex SHA-256 the workspace_source_pack receipt printed as address= (the archive's own digest; the fetched bytes must hash to it)" }, diff --git a/dag/gunbc/repo/repo_self_build.dag b/dag/gunbc/repo/repo_self_build.dag index ec33246b355..5a5bbade815 100644 --- a/dag/gunbc/repo/repo_self_build.dag +++ b/dag/gunbc/repo/repo_self_build.dag @@ -107,6 +107,30 @@ fn repo_self_test_command() -> String { ) } +// A NAMED SUBSET OF ONE PACKAGE'S UNIT TESTS, for a control in a package the whole-package run above +// does not cover: the blocking rust-unit-tests lane runs repo_self_test_command, which is +// v1-compiler only. +// The filters are libtest's substring filters after `--`; an empty list would run the whole +// package, which is repo_self_test_command's request and not this one. +fn repo_self_targeted_test_command(package: String, filters: List) -> String { + join(concat(["cargo test", repo_self_build_profile_flag, "-p", package, "--lib", "--"], filters), " ") +} + +// The crate the emitted runtime v1_rt.rs is compiled in (the stage0 partition's foundation crate). +data repo_self_runtime_package: String = "v1-stage0-runtime" + +// THE EMITTED RUNTIME'S SLICE COST CONTROLS, ENROLLED. v1_rt list_slice_tests carries a RED that runs +// the copying skip / take templates the emit table used to render and a GREEN that runs +// list_skip / list_take, on non-ASCII code points, so the pair discriminates the quadratic rather +// than merely exercising the slice. Only this crate is named: the interpreter's slice and drop +// controls (v1_interpreter slice_cost_tests, value_depth_walker_tests) are in v1-compiler's own +// unit tests, which the blocking rust-unit-tests lane already runs whole (repo_self_test_command), +// and running them again here would be the same work twice. v1-stage0-runtime is outside that +// lane's package, so without this step its controls would only compile. +fn repo_self_slice_cost_control_command() -> String { + repo_self_targeted_test_command(package: repo_self_runtime_package, filters: ["list_slice_tests"]) +} + // THE LINT PASS OVER EVERY TARGET, as the one invocation DESIGN's "Building & checks" names. // `--all-targets` is the whole point: the test and example targets outside `--lib` and `--bins` // are compiled by no build command above and executed by no test command, so before this row diff --git a/dag/gunbc/witness/compiler_gate_workflow.dag b/dag/gunbc/witness/compiler_gate_workflow.dag index 92c28cdff30..aa368984eb4 100644 --- a/dag/gunbc/witness/compiler_gate_workflow.dag +++ b/dag/gunbc/witness/compiler_gate_workflow.dag @@ -166,7 +166,7 @@ import gunbc.fleet_release_bins_key { release_bins_pack_object_script, release_bins_pack_object_path_expression, compiler_pair_candidate_artifact_name, compiler_pair_candidate_retention_whole_days, } -import gunbc.repo_self_build { repo_self_build_command, repo_self_clippy_command, repo_self_test_command } +import gunbc.repo_self_build { repo_self_build_command, repo_self_clippy_command, repo_self_test_command, repo_self_slice_cost_control_command } import extdeps.exec.command { shell_command_render } import extdeps.github.expressions { interpolate, ContextAccess, Needs, Github, Expression, default_expression_spellings } import extdeps.github.log_annotations { emit_line_shell } @@ -270,6 +270,28 @@ fn compiler_gate_hosted_build_prelude_bound_steps() -> List Step { + RunStep { + name: Present { value: compiler_gate_slice_cost_step_name }, + id: Present { value: compiler_gate_slice_cost_step_id }, + run: concat(wrap_command_with_floor_attempt_receipt(command: repo_self_slice_cost_control_command()), "\n"), + shell: none, + env: none, + working_directory: none, + if_condition: Present { value: witness_step_status_guard_expression() }, + continue_on_error: none, + timeout_minutes: none + } +} + fn compiler_gate_lint_step() -> Step { RunStep { name: Present { value: compiler_gate_clippy_step_name }, @@ -856,6 +878,11 @@ fn compiler_gate_generated_bound_steps() -> List { role: consumes_only(capabilities: [CargoCapability]), step_name: compiler_gate_clippy_step_name, }, + CompilerGateBoundStep { + step: compiler_gate_slice_cost_step(), + role: consumes_only(capabilities: [CargoCapability]), + step_name: compiler_gate_slice_cost_step_name, + }, CompilerGateBoundStep { step: compiler_gate_stage0_regen_step(), role: consumes_only(capabilities: [RustfmtCapability]), @@ -923,6 +950,7 @@ fn compiler_gate_floor_scripts_are_renderable() -> Bool { wrap_command_is_renderable(command: witness_floor_run_script()) && wrap_command_is_renderable(command: build_lane_run_script()) && wrap_command_is_renderable(command: repo_self_clippy_command()) + && wrap_command_is_renderable(command: repo_self_slice_cost_control_command()) && wrap_command_is_renderable(command: repo_self_build_command(bins: witness_floor_required_bins)) && required_ci_measurement_scripts_are_renderable() } diff --git a/dag/std/primitives.dag b/dag/std/primitives.dag index 70d251b14aa..e0c5cb86ca9 100644 --- a/dag/std/primitives.dag +++ b/dag/std/primitives.dag @@ -409,12 +409,31 @@ data append_contract: PrimitiveContract = { carrier_cost: CarrierSensitive { ephemeral_work: "n + m", persistent_work: "log(n + m)" } } +// SKIP AND TAKE ARE THE SLICE, AND ON THE PERSISTENT CARRIER THEY SHARE THE RECEIVER'S TREE. A walk +// that reads a code-point or octet list by offset slices it as `xs |> skip(n: a) |> take(n: b - a)`; +// when either half copied its result, each slice paid the remainder and the walk was quadratic in +// the list (DESIGN section 6, bare minimum cost). Both realizations hold the list as an RRB vector +// (the interpreter's Value::List, v1_rt's im::Vector), whose split is logarithmic, so the slice +// costs log len to form and only its own length to read. That removes the quadratic remainder +// copying; it does not make an offset walk linear, since every member read is get_contract's +// log n on this carrier, so a walk that reads each offset is O(n log n). +// +// THE COUNT IS CLAMPED, AND A NEGATIVE COUNT IS THE WHOLE LIST. Both realizations take +// k = if n < 0 then len else min(n, len): skip keeps len - k members and take keeps k. data skip_contract: PrimitiveContract = { name: "skip", - work: "n", - output_size: "max(0, len - n)", + work: "log len", + output_size: "len - k where k = if n < 0 then len else min(n, len)", certainty: Proven, - carrier_cost: CarrierInsensitive + carrier_cost: CarrierSensitive { ephemeral_work: "len - k where k = if n < 0 then len else min(n, len)", persistent_work: "log len" } +} + +data take_contract: PrimitiveContract = { + name: "take", + work: "log len", + output_size: "k where k = if n < 0 then len else min(n, len)", + certainty: Proven, + carrier_cost: CarrierSensitive { ephemeral_work: "k where k = if n < 0 then len else min(n, len)", persistent_work: "log len" } } data zip_contract: PrimitiveContract = { @@ -791,6 +810,7 @@ fn primitive_contract_roster() -> List { list_push_contract, append_contract, skip_contract, + take_contract, zip_contract, enumerate_contract, empty_map_contract, diff --git a/dag/test/claim/code_point_slice_witness_test.dag b/dag/test/claim/code_point_slice_witness_test.dag new file mode 100644 index 00000000000..eebf6e63af8 --- /dev/null +++ b/dag/test/claim/code_point_slice_witness_test.dag @@ -0,0 +1,94 @@ +module test.claim.code_point_slice_witness + +import std.algebra { Cons } +import std.coercion { unicode_scalar_unfold, unicode_scalar_fold } +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +// A BODY READ BY OFFSET, THROUGH THE CODE-POINT SLICE. The text is unfolded to its code points +// once; every later read is one member (`get`), the length (`count`) or a bounded slice +// (`skip` then `take`) folded back to host text. std.primitives get_contract, count_contract, +// skip_contract and take_contract price each of those at log len or better on the persistent +// carrier, so the walk is O(n log n) in the body on every input -- the remainder copying that made +// it quadratic is gone. Host-text char_at / substring are O(offset) per read on non-ASCII text in +// the interpreter and on all text in emitted Rust, which is the quadratic this surface avoids. +// The bodies here are non-ASCII on purpose: that is the input the host-text walk is quadratic on. +// +// The wire shape is the length-prefixed field `:`, repeated. +type CodePointField { + value: String + next: Int +} + +fn cps_find(cps: List, at: Int, stop: Int) -> Int? { + match get(xs: cps, index: at) { + Absent => none + Present { value: c } => if c == stop { Present { value: at } } else { cps_find(cps: cps, at: at + 1, stop: stop) } + } +} + +fn cps_slice_text(cps: List, start: Int, end: Int) -> String { + unicode_scalar_fold(xs: cps |> skip(n: start) |> take(n: end - start)) +} + +fn cps_field(cps: List, at: Int) -> CodePointField? { + match cps_find(cps: cps, at: at, stop: code_point(c: ":")) { + Absent => none + Present { value: colon } => + match parse_int(s: cps_slice_text(cps: cps, start: at, end: colon)) { + Absent => none + Present { value: n } => + if n < 0 || colon + 1 + n > count(cps) { + none + } else { + Present { value: CodePointField { value: cps_slice_text(cps: cps, start: colon + 1, end: colon + 1 + n), next: colon + 1 + n } } + } + } + } +} + +fn cps_fields(cps: List, at: Int, acc: List) -> List? { + if at == count(cps) { + Present { value: reverse(acc) } + } else { + match cps_field(cps: cps, at: at) { + Absent => none + Present { value: f } => cps_fields(cps: cps, at: f.next, acc: Cons { head: f.value, tail: acc }) + } + } +} + +fn decode_fields(body: String) -> List? { + cps_fields(cps: unicode_scalar_unfold(s: body), at: 0, acc: []) +} + +// The body doubled `times` times: linear to build, so the measured walk is the only quadratic +// candidate in the claim. +fn doubled(s: String, times: Int) -> String { + if times <= 0 { s } else { doubled(s: concat(s, s), times: times - 1) } +} + +// POSITIVE CONTROL: the slice reads the right code points, including astral ones (one code point, +// four UTF-8 bytes) and a field whose length prefix is two digits. +test fn code_point_slice_decodes_non_ascii_fields() -> Bool { + decode_fields(body: "2:\u{e9}\u{4e2d}3:\u{1f600}ab0:12:\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}") == Present { value: ["\u{e9}\u{4e2d}", "\u{1f600}ab", "", "\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}\u{e9}"] } +} + +// A length prefix that runs past the body is refused, not truncated. +test fn code_point_slice_refuses_an_overlong_field() -> Bool { + decode_fields(body: "4:\u{e9}\u{4e2d}") == none +} + +// THE WALK OVER MANY FIELDS: 512 non-ASCII fields, 3,072 code points, sized under the floor's +// new-witness eval-step budget (v2.workflow.required_floor). This claim's subject is that the +// decode stays correct field after field; the COST of the walk is not measured here, because eval +// steps cannot see a host-level copy. The cost controls that can, RED on the copying forms and +// GREEN on the sharing ones, are gunbc.repo_self_build repo_self_slice_cost_control_command, run on +// the merge path by the generated job's slice cost step. +test fn code_point_slice_walk_is_correct_at_scale() -> Bool { + match decode_fields(body: doubled(s: "3:\u{e9}\u{4e2d}\u{1f600}", times: 9)) { + Absent => false + Present { value: fs } => count(fs) == 512 && fs |> all(f => f == "\u{e9}\u{4e2d}\u{1f600}") + } +} diff --git a/dag/test/claim/seeded_filler_real_execution_witness_test.dag b/dag/test/claim/seeded_filler_real_execution_witness_test.dag new file mode 100644 index 00000000000..2e646e53c23 --- /dev/null +++ b/dag/test/claim/seeded_filler_real_execution_witness_test.dag @@ -0,0 +1,94 @@ +module test.claim.seeded_filler_real_execution + +import std.types { String, NonEmptyStr, Int, Bool, FilePath } +import std.measure { ByteSize, byte_size, measure_count } +import std.optional { Present, Absent } +import extdeps.shell +import extdeps.tools.coreutils_stat +import extdeps.tools.coreutils_truncate +import extdeps.tools.gzip +import extdeps.tools.sha256sum +import extdeps.crypto.seeded_filler { seeded_filler_write, SeededFillerWritten, SeededFillerRefused } +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +// THE SEEDED FILLER BY REAL EXECUTION: truncate and openssl enc run on the host, inside a directory +// the witness mints and removes. Three facts the workspace pack relies on: the size is exact, one +// seed reproduces its bytes, and two seeds give different bytes. The red control is the reason the +// filler exists at all: a zero file of the same size -- what truncate alone would leave -- gzips to +// almost nothing, while the filler does not shrink, so a zero or patterned stand-in would understate +// what kopia and the chunk store pay to carry the real content. + +fn with_temp_dir(scenario: fn(String) -> Bool) -> Bool { + let dir = shell.Mktemp.Dir() + if dir.success == false { + false + } else { + let observed = scenario(dir.path as String) + let cleaned = shell.Remove.RecursiveForce(path: dir.path) + observed && cleaned.success + } +} + +fn written_at(seed: String, size: Int, path: String) -> Bool { + match seeded_filler_write(seed: seed as NonEmptyStr, size: byte_size(count: size), path: path as NonEmptyStr) { + SeededFillerWritten { path: p, size: s } => (p as String) == path && measure_count(m: s) == size + SeededFillerRefused { path: _, step: _, detail: _ } => false + } +} + +fn stat_size(path: String) -> Int { + let st = coreutils.Stat.PathSize(path: path as NonEmptyStr) + match parse_int(s: replace(st.size_text, "\n", "")) { + Present { value: n } => if st.success { n } else { -1 } + Absent => -1 + } +} + +fn digest_of(path: String) -> String { + let d = sha256sum.Sha256.DigestFile(path: path as NonEmptyStr) + if d.success { substring(s: d.line, start: 0, end: 64) } else { join(["unread:", path], "") } +} + +fn gzipped_size(path: String) -> Int { + if gzip.Compress.BesideKeepBest(path: path as NonEmptyStr).success { stat_size(path: concat(path, ".gz")) } else { -1 } +} + +test fn a_seeded_filler_is_exactly_its_size_and_reproducible_by_real_execution() -> Bool { + with_temp_dir(scenario: dir => { + let a1 = join([dir, "/a1"], "") + let a2 = join([dir, "/a2"], "") + let one = join([dir, "/one"], "") + let empty = join([dir, "/empty"], "") + written_at(seed: "home/u/.claude/projects/p/s.jsonl", size: 70001, path: a1) + && written_at(seed: "home/u/.claude/projects/p/s.jsonl", size: 70001, path: a2) + && written_at(seed: "x", size: 1, path: one) + && written_at(seed: "x", size: 0, path: empty) + && stat_size(path: a1) == 70001 && stat_size(path: one) == 1 && stat_size(path: empty) == 0 + && digest_of(path: a1) == digest_of(path: a2) + }) +} + +test fn two_seeds_give_different_fillers_by_real_execution() -> Bool { + with_temp_dir(scenario: dir => { + let a = join([dir, "/a"], "") + let b = join([dir, "/b"], "") + written_at(seed: "home/u/.claude/history.jsonl", size: 4096, path: a) + && written_at(seed: "home/u/.codex/history.jsonl", size: 4096, path: b) + && digest_of(path: a) != digest_of(path: b) + }) +} + +// THE RED CONTROL: zeros of the same size compress to a sliver; the filler does not compress at all. +test fn a_zero_stand_in_compresses_and_the_filler_does_not_red_control_by_real_execution() -> Bool { + with_temp_dir(scenario: dir => { + let filler = join([dir, "/filler"], "") + let zeros = join([dir, "/zeros"], "") + let size = 262144 + written_at(seed: "home/u/.claude/projects/p/s.jsonl", size: size, path: filler) + && coreutils.Truncate.ToSize(size_bytes: to_string(size) as NonEmptyStr, path: zeros as NonEmptyStr).success + && gzipped_size(path: zeros) > 0 && gzipped_size(path: zeros) < size / 100 + && gzipped_size(path: filler) >= size + }) +} diff --git a/dag/test/claim/workspace_checkpoint_instrument_witness_test.dag b/dag/test/claim/workspace_checkpoint_instrument_witness_test.dag index d3d9bfe29e1..ee9f77f24de 100644 --- a/dag/test/claim/workspace_checkpoint_instrument_witness_test.dag +++ b/dag/test/claim/workspace_checkpoint_instrument_witness_test.dag @@ -5,6 +5,7 @@ import std.optional { Present, Absent } import std.algebra { Cons, Empty } import std.measure { byte_size, measure_count, Nanosecond, nanosecond, nanosecond_count } import std.content_hash { Sha256Digest, sha256_hex_digest } +import v2.std.collection { Map, empty_map, map_insert } import std.fabric_blob { FabricBlobAddress } import std.fabric_storage { FabricObject, FabricStoredObject, FabricObjectFetched, FabricObjectGetRefused, FabricObjectMissing, fabric_object_ref_of, fabric_object_ref_eq } import std.durable_compare_and_set { cas_generation_first } @@ -24,12 +25,16 @@ import gunbc.auth.access_token_source { WorkloadIdentityToken } import gunbc.fabric_storage_r2_head { R2HeadCredential, r2_head_binding, R2HeadCasNotGrounded, R2HeadAdvanceKnown, R2HeadAdvancedTo, R2HeadStagingCustody } import gunbc.fabric.workspace_source { WorkspaceSourceRoot, workspace_credential_exclusions, workspace_exclusions, workspace_path_exclusion, workspace_source_tally, - workspace_pack_readback, WorkspacePackClean, WorkspacePackCarriesExcluded, + workspace_pack_readback, WorkspacePackReadback, WorkspacePackClean, WorkspacePackCarriesExcluded, WorkspacePackPlaceholderAbsent, WorkspacePackPlaceholderSizeDiffers, + WorkspacePackPlaceholderNotRegular, WorkspacePackPlaceholderUnread, WorkspacePackPlaceholderDigestDiffers, WorkspaceStagedPlaceholder, + workspace_transcript_caveat, + workspace_observed_agent_state_slots, workspace_agent_state_slot_admission, workspace_agent_state_slot_words, + AgentStateSlotAdmitted, AgentStateSlotAbsent, AgentStateSlotUnknown, } import gunbc.fabric.workspace_checkpoint_instrument { workspace_snapshot_capability, WorkspaceSnapshotBtrfs, WorkspaceSnapshotAbsent, WorkspaceSnapshotUnobserved, WorkspaceMeasuredPart, PartMeasured, PartUnmeasured, workspace_bound_verdict, BoundHolds, BoundExceeded, BoundUndecided, - workspace_quota_verdict, QuotaHolds, QuotaExceeded, + workspace_quota_verdict, QuotaHolds, QuotaExceeded, workspace_draft_verdict_lines, workspace_draft_numbers, workspace_instrument_commit, InstrumentCommitCasNotGrounded, WorkspaceCustody, workspace_custody_clean, workspace_custody_of_head_advance, workspace_exit_with_custody, WorkspaceRestoredFile, workspace_restore_compare, RestoreByteEqual, RestoreMissingFile, RestoreExtraFile, RestoreBytesDiffer, RestoreCarriesUncommittedChunk, @@ -180,22 +185,195 @@ test fn the_tally_admits_only_unexcluded_non_build_files() -> Bool { && map(t.excluded_credential_members, m => m as String) == ["home/u/gunbc/.env", "home/u/gunbc/keys/id_rsa"] && t.excluded_transcripts == 2 && measure_count(m: t.excluded_transcript_bytes) == 5700 && t.build_output == 1 && t.unparsed == [] + && map(t.transcript_placeholders, p => join([p.member as String, "=", to_string(measure_count(m: p.size))], "")) == ["home/u/gunbc/projects/p/0d3a.jsonl=5000", "home/u/gunbc/history.jsonl=700"] +} + +// A CREDENTIAL NEVER GETS A PLACEHOLDER, NOT EVEN ONE THAT ALSO LOOKS LIKE A TRANSCRIPT: credentials +// are matched first, and only the TranscriptExclusion class mints a placeholder. A transcript +// symlink carries no content to stand in for, so it gets none either. +test fn a_credential_class_member_never_gets_a_placeholder() -> Bool { + let listing = join([ + "f\t600\t.credentials.json\t\t450", + "f\t600\tprojects/p/.env\t\t30", + "f\t600\tprojects/p/id_rsa.jsonl\t\t0", + "f\t600\tprojects/.ssh/k.jsonl\t\t80", + "l\t777\tprojects/p/link.jsonl\tother.jsonl\t11", + "f\t644\tprojects/p/s.jsonl\t\t9", + ], "\n") + let t = workspace_source_tally(root: tally_root, exclusions: workspace_exclusions(), listing: listing) + t.excluded_credentials == 3 + && map(t.transcript_placeholders, p => p.member as String) == ["home/u/gunbc/projects/p/id_rsa.jsonl", "home/u/gunbc/projects/p/s.jsonl"] + && t.excluded_transcripts == 3 +} + +data readback_member: String = "home/u/.claude/projects/p/s 1.jsonl" + +data readback_placeholder: WorkspaceStagedPlaceholder = WorkspaceStagedPlaceholder { member: "home/u/.claude/projects/p/s 1.jsonl", size: byte_size(count: 5000), expected_digest: digest(c: "a") } + +data readback_names: String = "home/u/gunbc/README.md\nhome/u/.claude/projects/p/s 1.jsonl\n" + +fn readback_sizes(type_letter: String, placeholder_bytes: String) -> String { + join([ + "-rw-r--r-- 1000/1000 120 2026-10-05 12:00:01 home/u/gunbc/README.md", + join([type_letter, "rw-r--r-- 1000/1000 ", placeholder_bytes, " 2026-10-05 12:00:02 home/u/.claude/projects/p/s 1.jsonl"], ""), + "", + ], "\n") +} + +fn archived_digest(hex_char: String) -> Map { + map_insert(m: empty_map(), key: readback_member, value: digest(c: hex_char).hex as String) +} + +fn readback_of(placeholders: List, names: String, sizes: String, digests: Map) -> WorkspacePackReadback { + workspace_pack_readback(exclusions: workspace_exclusions(), placeholders: placeholders, archive_listing: names, size_listing: sizes, archived_digests: digests) } // THE PACK READBACK'S RED: an archive that carries a credential refuses the upload, whatever list // built it. test fn an_archive_carrying_a_credential_refuses_the_upload() -> Bool { - match workspace_pack_readback(exclusions: workspace_exclusions(), archive_listing: "home/u/gunbc/README.md\nsession-home/.claude/.credentials.json\n") { + match readback_of(placeholders: [], names: "home/u/gunbc/README.md\nsession-home/.claude/.credentials.json\n", sizes: "", digests: empty_map()) { WorkspacePackCarriesExcluded { member: m, by: x } => (m as String) == "session-home/.claude/.credentials.json" && (x.spelling as String) == "~/.claude/.credentials.json" WorkspacePackClean { members: _ } => false + WorkspacePackPlaceholderAbsent { member: _ } => false + WorkspacePackPlaceholderNotRegular { member: _, type_letter: _ } => false + WorkspacePackPlaceholderSizeDiffers { member: _, planned: _, archived: _ } => false + WorkspacePackPlaceholderUnread { member: _ } => false + WorkspacePackPlaceholderDigestDiffers { member: _, expected: _, archived: _ } => false + } +} + +// A TRANSCRIPT NAME WITH NO STAGED PLACEHOLDER IS REAL CONTENT AND REFUSES. +test fn an_archived_transcript_without_a_placeholder_refuses_the_upload() -> Bool { + match readback_of(placeholders: [], names: readback_names, sizes: readback_sizes(type_letter: "-", placeholder_bytes: "5000"), digests: archived_digest(hex_char: "a")) { + WorkspacePackCarriesExcluded { member: m, by: x } => (m as String) == readback_member && (x.spelling as String) == "projects/**/*.jsonl" + WorkspacePackClean { members: _ } => false + WorkspacePackPlaceholderAbsent { member: _ } => false + WorkspacePackPlaceholderNotRegular { member: _, type_letter: _ } => false + WorkspacePackPlaceholderSizeDiffers { member: _, planned: _, archived: _ } => false + WorkspacePackPlaceholderUnread { member: _ } => false + WorkspacePackPlaceholderDigestDiffers { member: _, expected: _, archived: _ } => false + } +} + +// THE CONFIDENTIALITY RED: a REAL transcript of the placeholder's exact name, type and size is +// refused, because its bytes do not hash to the staged filler's digest. +test fn a_same_size_real_content_member_refuses_the_upload_by_digest() -> Bool { + match readback_of(placeholders: [readback_placeholder], names: readback_names, sizes: readback_sizes(type_letter: "-", placeholder_bytes: "5000"), digests: archived_digest(hex_char: "b")) { + WorkspacePackPlaceholderDigestDiffers { member: m, expected: e, archived: a } => (m as String) == readback_member && (e.hex as String) == (digest(c: "a").hex as String) && a == (digest(c: "b").hex as String) + WorkspacePackClean { members: _ } => false + WorkspacePackCarriesExcluded { member: _, by: _ } => false + WorkspacePackPlaceholderAbsent { member: _ } => false + WorkspacePackPlaceholderNotRegular { member: _, type_letter: _ } => false + WorkspacePackPlaceholderSizeDiffers { member: _, planned: _, archived: _ } => false + WorkspacePackPlaceholderUnread { member: _ } => false + } +} + +// THE PLACEHOLDER'S SIZE RED: archived at any size but its planned one, the upload refuses. +test fn a_placeholder_whose_size_differs_refuses_the_upload() -> Bool { + match readback_of(placeholders: [readback_placeholder], names: readback_names, sizes: readback_sizes(type_letter: "-", placeholder_bytes: "4999"), digests: archived_digest(hex_char: "a")) { + WorkspacePackPlaceholderSizeDiffers { member: m, planned: p, archived: a } => (m as String) == readback_member && measure_count(m: p) == 5000 && measure_count(m: a) == 4999 + WorkspacePackClean { members: _ } => false + WorkspacePackCarriesExcluded { member: _, by: _ } => false + WorkspacePackPlaceholderAbsent { member: _ } => false + WorkspacePackPlaceholderNotRegular { member: _, type_letter: _ } => false + WorkspacePackPlaceholderUnread { member: _ } => false + WorkspacePackPlaceholderDigestDiffers { member: _, expected: _, archived: _ } => false + } +} + +test fn a_placeholder_archived_as_a_non_regular_member_refuses_the_upload() -> Bool { + match readback_of(placeholders: [readback_placeholder], names: readback_names, sizes: readback_sizes(type_letter: "c", placeholder_bytes: "5000"), digests: archived_digest(hex_char: "a")) { + WorkspacePackPlaceholderNotRegular { member: m, type_letter: t } => (m as String) == readback_member && t == "c" + WorkspacePackClean { members: _ } => false + WorkspacePackCarriesExcluded { member: _, by: _ } => false + WorkspacePackPlaceholderAbsent { member: _ } => false + WorkspacePackPlaceholderSizeDiffers { member: _, planned: _, archived: _ } => false + WorkspacePackPlaceholderUnread { member: _ } => false + WorkspacePackPlaceholderDigestDiffers { member: _, expected: _, archived: _ } => false } } -test fn a_clean_archive_reads_back_its_member_count() -> Bool { - match workspace_pack_readback(exclusions: workspace_exclusions(), archive_listing: "home/u/gunbc/README.md\nhome/u/gunbc/src/a.rs\n") { +test fn a_placeholder_whose_archived_bytes_were_not_read_refuses_the_upload() -> Bool { + match readback_of(placeholders: [readback_placeholder], names: readback_names, sizes: readback_sizes(type_letter: "-", placeholder_bytes: "5000"), digests: empty_map()) { + WorkspacePackPlaceholderUnread { member: m } => (m as String) == readback_member + WorkspacePackClean { members: _ } => false + WorkspacePackCarriesExcluded { member: _, by: _ } => false + WorkspacePackPlaceholderAbsent { member: _ } => false + WorkspacePackPlaceholderNotRegular { member: _, type_letter: _ } => false + WorkspacePackPlaceholderSizeDiffers { member: _, planned: _, archived: _ } => false + WorkspacePackPlaceholderDigestDiffers { member: _, expected: _, archived: _ } => false + } +} + +test fn a_planned_placeholder_missing_from_the_archive_refuses_the_upload() -> Bool { + match readback_of(placeholders: [readback_placeholder], names: "home/u/gunbc/README.md\n", sizes: "-rw-r--r-- 1000/1000 120 2026-10-05 12:00:01 home/u/gunbc/README.md\n", digests: empty_map()) { + WorkspacePackPlaceholderAbsent { member: m } => (m as String) == readback_member + WorkspacePackClean { members: _ } => false + WorkspacePackCarriesExcluded { member: _, by: _ } => false + WorkspacePackPlaceholderNotRegular { member: _, type_letter: _ } => false + WorkspacePackPlaceholderSizeDiffers { member: _, planned: _, archived: _ } => false + WorkspacePackPlaceholderUnread { member: _ } => false + WorkspacePackPlaceholderDigestDiffers { member: _, expected: _, archived: _ } => false + } +} + +// THE POSITIVE CONTROL: the placeholder at its name, regular, at size, hashing to its staged digest. +test fn a_clean_archive_with_its_placeholder_intact_reads_back_its_member_count() -> Bool { + match readback_of(placeholders: [readback_placeholder], names: readback_names, sizes: readback_sizes(type_letter: "-", placeholder_bytes: "5000"), digests: archived_digest(hex_char: "a")) { WorkspacePackClean { members: n } => n == 2 WorkspacePackCarriesExcluded { member: _, by: _ } => false + WorkspacePackPlaceholderAbsent { member: _ } => false + WorkspacePackPlaceholderNotRegular { member: _, type_letter: _ } => false + WorkspacePackPlaceholderSizeDiffers { member: _, planned: _, archived: _ } => false + WorkspacePackPlaceholderUnread { member: _ } => false + WorkspacePackPlaceholderDigestDiffers { member: _, expected: _, archived: _ } => false + } +} + +// ── THE AGENT-STATE SLOT: A DECLARED INPUT, A CLOSED CHOICE OVER THE OBSERVED SLOTS ─────────── + +data observed_home_dirs: List = ["/home/briansrls/.claude@gmail2", "/home/briansrls/.claude@gmail3", "/home/briansrls/.claude", "/home/briansrls/.claude@", "/home/briansrls/gunbc", "/home/briansrls/.codex"] + +test fn the_observed_slots_are_the_claude_at_directories() -> Bool { + workspace_observed_agent_state_slots(dirs: observed_home_dirs) == ["gmail2", "gmail3"] +} + +// A NAMED EXISTING SLOT IS ADMITTED, as that slot's root, and the receipt line carries slot and path. +test fn a_named_existing_slot_is_admitted_and_its_receipt_carries_it() -> Bool { + let a = workspace_agent_state_slot_admission(requested: Present { value: "gmail2" }, observed: workspace_observed_agent_state_slots(dirs: observed_home_dirs)) + let admitted = match a { + AgentStateSlotAdmitted { slot: s, root: r } => (s as String) == "gmail2" && (r.path as String) == "/home/briansrls/.claude@gmail2" && (r.label as String) == "claude-agent-state-gmail2" + AgentStateSlotAbsent { observed: _ } => false + AgentStateSlotUnknown { requested: _, observed: _ } => false } + admitted && workspace_agent_state_slot_words(admission: a) == "agent_state_slot=gmail2 path=/home/briansrls/.claude@gmail2" +} + +// NO DEFAULT AND NO FALLBACK: an absent (or empty) slot refuses, naming what was observed. +test fn an_absent_slot_refuses_naming_the_observed_slots() -> Bool { + let observed = workspace_observed_agent_state_slots(dirs: observed_home_dirs) + let absent = match workspace_agent_state_slot_admission(requested: none, observed: observed) { + AgentStateSlotAbsent { observed: o } => o == ["gmail2", "gmail3"] + AgentStateSlotAdmitted { slot: _, root: _ } => false + AgentStateSlotUnknown { requested: _, observed: _ } => false + } + let empty = match workspace_agent_state_slot_admission(requested: Present { value: "" }, observed: observed) { + AgentStateSlotAbsent { observed: _ } => true + AgentStateSlotAdmitted { slot: _, root: _ } => false + AgentStateSlotUnknown { requested: _, observed: _ } => false + } + absent && empty +} + +test fn an_unknown_slot_refuses_naming_the_observed_slots() -> Bool { + let a = workspace_agent_state_slot_admission(requested: Present { value: "gmail9" }, observed: workspace_observed_agent_state_slots(dirs: observed_home_dirs)) + let refused = match a { + AgentStateSlotUnknown { requested: r, observed: o } => r == "gmail9" && o == ["gmail2", "gmail3"] + AgentStateSlotAdmitted { slot: _, root: _ } => false + AgentStateSlotAbsent { observed: _ } => false + } + refused && string_contains(s: workspace_agent_state_slot_words(admission: a), pattern: "observed=gmail2,gmail3") } // ── VERDICTS ─────────────────────────────────────────────────────────────────────────────────── @@ -224,6 +402,20 @@ test fn measured_parts_over_the_bound_exceed_it_whatever_is_missing() -> Bool { } } +// WHILE THE SUBJECT IS A STATIC TREE WITH SYNTHETIC TRANSCRIPTS, THE 10 s / 60 s VERDICTS ARE +// WITHHELD -- even when every measured part is far under both bounds, no bound line says it holds -- +// and the quota decides over the static size, labelled static. +test fn the_draft_time_verdicts_are_withheld_and_the_quota_is_static() -> Bool { + let fast = [PartMeasured { label: "snapshot", elapsed: ms(n: 40) }, PartMeasured { label: "upload", elapsed: ms(n: 900) }, PartMeasured { label: "commit", elapsed: ms(n: 20) }] + let block = workspace_draft_verdict_lines(snapshot_parts: fast, scan_parts: fast, static_size: Present { value: byte_size(count: 1600000000) }, n: workspace_draft_numbers) + let lines = split(block, separator: "\n") + string_contains(s: block, pattern: workspace_transcript_caveat as String) + && length(filter(lines, l => string_contains(s: l, pattern: "=withheld: churn not measured"))) == 4 + && length(filter(lines, l => string_contains(s: l, pattern: "=holds") || string_contains(s: l, pattern: "=exceeded") || string_contains(s: l, pattern: "=undecided"))) == 1 + && string_contains(s: block, pattern: "quota_static=holds bytes=1600000000") + && (workspace_transcript_caveat as String) == "transcript content synthetic, sizes preserved, static tree; NOT a 10 s / 60 s verdict" +} + test fn the_quota_verdict_compares_bytes_to_the_quota() -> Bool { let under = match workspace_quota_verdict(size: byte_size(count: 1600000000), quota: byte_size(count: 10000000000)) { QuotaHolds { size: _, quota: _ } => true QuotaExceeded { size: _, quota: _ } => false } let over = match workspace_quota_verdict(size: byte_size(count: 10000000001), quota: byte_size(count: 10000000000)) { QuotaExceeded { size: _, quota: _ } => true QuotaHolds { size: _, quota: _ } => false } diff --git a/dag/test/claim/workspace_source_pack_archive_real_execution_witness_test.dag b/dag/test/claim/workspace_source_pack_archive_real_execution_witness_test.dag new file mode 100644 index 00000000000..4663e1efb0e --- /dev/null +++ b/dag/test/claim/workspace_source_pack_archive_real_execution_witness_test.dag @@ -0,0 +1,110 @@ +module test.claim.workspace_source_pack_archive_real_execution + +import std.types { String, NonEmptyStr, Int, Bool, List, FilePath } +import std.measure { byte_size } +import extdeps.filesystem.filesystem_io { Filesystem } +import extdeps.shell +import gunbc.fabric.workspace_source { + WorkspaceTranscriptPlaceholder, WorkspaceStagedPlaceholder, workspace_stage_placeholders, WorkspacePlaceholdersStaged, WorkspacePlaceholdersRefused, + workspace_pack_archive, workspace_pack_refusal_words, WorkspacePackBuilt, WorkspacePackRefused, + WorkspacePackClean, WorkspacePackCarriesExcluded, WorkspacePackPlaceholderAbsent, WorkspacePackPlaceholderNotRegular, + WorkspacePackPlaceholderSizeDiffers, WorkspacePackPlaceholderUnread, WorkspacePackPlaceholderDigestDiffers, +} +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +// THE PACK'S TWO-ROOT TAR COMPOSITION AND ITS ARCHIVE READBACK, BY REAL EXECUTION, inside a directory +// the witness mints and removes. A real tree (one source file, and a real transcript the plan +// excludes) and a staged placeholder for that transcript are packed into one tar by the production +// gunbc.fabric.workspace_source workspace_pack_archive, which lists the archive back, extracts the +// placeholder member and hashes it. The positive control reads back clean. The red is the +// confidentiality boundary itself: the staged placeholder's bytes are swapped for real content of +// the SAME size before packing -- what a real transcript standing at the placeholder's path would +// look like -- and the archive is refused by digest. The pure readback arms are discriminated in +// test.claim.workspace_checkpoint_instrument; this witness is their inhabitance on the real route. + +fn with_temp_dir(scenario: fn(String) -> Bool) -> Bool { + let dir = shell.Mktemp.Dir() + if dir.success == false { + false + } else { + let observed = scenario(dir.path as String) + let cleaned = shell.Remove.RecursiveForce(path: dir.path) + observed && cleaned.success + } +} + +fn relative_to_root(path: String) -> String { + substring(s: path, start: 1, end: length(path)) +} + +data transcript_text: String = "{\"type\":\"user\",\"message\":\"a real transcript line of exactly sixty-four bytes\"}\n" + +// The real tree under

/real and the placeholder plan for its one transcript, at the +// transcript's own size. Returns the plan only if the tree was written. +fn plant(dir: String) -> List { + let real = join([dir, "/real/.claude/projects/p"], "") + let made = shell.Mkdir.Parents(path: real as FilePath).success + let readme = Filesystem.Write(path: join([dir, "/real/README.md"], ""), content: "readme\n").success + let transcript = Filesystem.Write(path: join([real, "/s.jsonl"], ""), content: transcript_text).success + if made && readme && transcript { + [WorkspaceTranscriptPlaceholder { member: relative_to_root(path: join([real, "/s.jsonl"], "")) as NonEmptyStr, size: byte_size(count: length(transcript_text)) }] + } else { + [] + } +} + +fn pack(dir: String, plan: List, swap_in_real_content: Bool) -> String { + let staging = join([dir, "/staging"], "") + let root = join([staging, "/placeholders"], "") + match workspace_stage_placeholders(root: root, placeholders: plan, staged: []) { + WorkspacePlaceholdersRefused { why: w } => concat("staging-refused: ", w) + WorkspacePlaceholdersStaged { placeholders: staged } => { + let swapped = if swap_in_real_content { + Filesystem.Write(path: join([root, "/", relative_to_root(path: join([dir, "/real/.claude/projects/p/s.jsonl"], ""))], ""), content: transcript_text).success + } else { true } + if swapped == false { + "swap-failed" + } else { + match workspace_pack_archive(staging: staging, members: [relative_to_root(path: join([dir, "/real/README.md"], "")) as NonEmptyStr], placeholders: staged, placeholder_root: root) { + WorkspacePackRefused { refusal: v, why: w } => join(["pack-refused: ", workspace_pack_refusal_words(refusal: v), " ", w], "") + WorkspacePackBuilt { archive: _, readback: r } => + match r { + WorkspacePackClean { members: n } => concat("clean ", to_string(n)) + WorkspacePackCarriesExcluded { member: _, by: _ } => "carries-excluded" + WorkspacePackPlaceholderAbsent { member: _ } => "placeholder-absent" + WorkspacePackPlaceholderNotRegular { member: _, type_letter: _ } => "placeholder-not-regular" + WorkspacePackPlaceholderSizeDiffers { member: _, planned: _, archived: _ } => "placeholder-size-differs" + WorkspacePackPlaceholderUnread { member: _ } => "placeholder-unread" + WorkspacePackPlaceholderDigestDiffers { member: _, expected: _, archived: _ } => "placeholder-digest-differs" + } + } + } + } + } +} + +test fn the_two_root_archive_carries_the_placeholder_and_reads_back_clean_by_real_execution() -> Bool { + with_temp_dir(scenario: dir => { + let plan = plant(dir: dir) + length(plan) == 1 && pack(dir: dir, plan: plan, swap_in_real_content: false) == "clean 2" + }) +} + +test fn same_size_real_content_at_the_placeholder_path_is_refused_by_digest_by_real_execution() -> Bool { + with_temp_dir(scenario: dir => { + let plan = plant(dir: dir) + length(plan) == 1 && pack(dir: dir, plan: plan, swap_in_real_content: true) == "placeholder-digest-differs" + }) +} + +// A WORKSPACE WITH NO TRANSCRIPTS: one ordinary file and zero placeholders packs clean through the +// same two-list route, its second list empty. +test fn one_ordinary_file_and_zero_placeholders_packs_clean_by_real_execution() -> Bool { + with_temp_dir(scenario: dir => { + let made = shell.Mkdir.Parents(path: join([dir, "/real"], "") as FilePath).success + let readme = Filesystem.Write(path: join([dir, "/real/README.md"], ""), content: "readme\n").success + made && readme && pack(dir: dir, plan: [], swap_in_real_content: false) == "clean 1" + }) +} diff --git a/dag/test/claim/workspace_transcript_rate_real_execution_witness_test.dag b/dag/test/claim/workspace_transcript_rate_real_execution_witness_test.dag new file mode 100644 index 00000000000..59ae0b07514 --- /dev/null +++ b/dag/test/claim/workspace_transcript_rate_real_execution_witness_test.dag @@ -0,0 +1,87 @@ +module test.claim.workspace_transcript_rate_real_execution + +import std.types { String, NonEmptyStr, Int, Bool, List, FilePath } +import std.measure { measure_count } +import std.algebra { Cons, Empty } +import std.checkpointed_workspace { WorkspaceAgeOf, WorkspaceAgeUndefined, workspace_age_between } +import extdeps.filesystem.filesystem_io { Filesystem } +import extdeps.shell +import gunbc.fabric.workspace_source { WorkspaceSourceRoot } +import gunbc.fabric.workspace_transcript_rate { + workspace_transcript_rate_sample_wet, workspace_transcript_rate_interval, TranscriptAppendInterval, + TranscriptRateSampled, TranscriptRateSampleRefused, TranscriptRateSample, +} +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +// THE INHABITANCE OF THE PURE BOUNDARIES IN test.claim.workspace_transcript_rate (DESIGN 3: supplied +// inputs never remove the last execution of the real path). A real agent-state tree is planted in a +// directory the witness mints and removes, and the production sampler -- the real find walk, the +// pack's real matcher -- reads it, is written to, and reads it again. The route is asserted, not only +// the answer: the transcript grows by exactly the bytes written, a new transcript appears at its +// whole size, a file outside the transcript class is never observed however much it grows, and a +// transcript rewritten shorter is a truncation. A root that does not exist refuses through find's +// own status. + +fn with_temp_dir(scenario: fn(String) -> Bool) -> Bool { + let dir = shell.Mktemp.Dir() + if dir.success == false { + false + } else { + let observed = scenario(dir.path as String) + let cleaned = shell.Remove.RecursiveForce(path: dir.path) + observed && cleaned.success + } +} + +fn root_at(dir: String) -> WorkspaceSourceRoot { + WorkspaceSourceRoot { label: "claude-agent-state-witness", path: join([dir, "/.claude"], "") as NonEmptyStr, build_output: [] } +} + +fn interval_of(before: TranscriptRateSample, after: TranscriptRateSample) -> List { + match before { + TranscriptRateSampleRefused { refusals: _ } => [] + TranscriptRateSampled { at: a, files: fa } => + match after { + TranscriptRateSampleRefused { refusals: _ } => [] + TranscriptRateSampled { at: b, files: fb } => + match workspace_age_between(from: a, to: b) { + WorkspaceAgeUndefined { order: _ } => [] + WorkspaceAgeOf { age: span } => [workspace_transcript_rate_interval(before: fa, after: fb, span: span)] + } + } + } +} + +test fn a_real_tree_grows_gains_a_transcript_and_truncates_one_by_real_execution() -> Bool { + with_temp_dir(scenario: dir => { + let projects = join([dir, "/.claude/projects/p"], "") + let planted = shell.Mkdir.Parents(path: projects as FilePath).success + && Filesystem.Write(path: join([projects, "/s.jsonl"], ""), content: "0123456789").success + && Filesystem.Write(path: join([projects, "/t.jsonl"], ""), content: "0123456789").success + && Filesystem.Write(path: join([dir, "/.claude/settings.json"], ""), content: "{}").success + let first = workspace_transcript_rate_sample_wet(roots: [root_at(dir: dir)], label: "transcript-rate-witness-first") + let written = Filesystem.Write(path: join([projects, "/s.jsonl"], ""), content: "0123456789abcdefghij").success + && Filesystem.Write(path: join([projects, "/t.jsonl"], ""), content: "0123").success + && Filesystem.Write(path: join([projects, "/u.jsonl"], ""), content: "01234").success + && Filesystem.Write(path: join([dir, "/.claude/settings.json"], ""), content: "{\"a\":\"a much longer settings file\"}").success + let second = workspace_transcript_rate_sample_wet(roots: [root_at(dir: dir)], label: "transcript-rate-witness-second") + planted && written && (match interval_of(before: first, after: second) { + Cons { head: row, tail: Empty } => + measure_count(m: row.bytes_appended) == 15 + && row.files_grown == 1 && row.files_created == 1 && length(row.truncations) == 1 && row.active_writers == 3 + Cons { head: _, tail: Cons { head: _, tail: _ } } => false + Empty => false + }) + }) +} + +test fn a_root_that_does_not_exist_refuses_by_real_execution() -> Bool { + with_temp_dir(scenario: dir => { + match workspace_transcript_rate_sample_wet(roots: [root_at(dir: dir)], label: "transcript-rate-witness-absent") { + TranscriptRateSampleRefused { refusals: rs } => length(rs) == 1 + TranscriptRateSampled { at: _, files: _ } => false + } + }) +} diff --git a/dag/test/claim/workspace_transcript_rate_witness_test.dag b/dag/test/claim/workspace_transcript_rate_witness_test.dag new file mode 100644 index 00000000000..a73788bf019 --- /dev/null +++ b/dag/test/claim/workspace_transcript_rate_witness_test.dag @@ -0,0 +1,205 @@ +module test.claim.workspace_transcript_rate + +import std.types { String, NonEmptyStr, Int, Bool, List } +import std.measure { InstantBefore, InstantSame, byte_size, measure_count, nanosecond, nanosecond_count, ObservationInstant, ProcessMonotonic, WallClock, CpuClock, InstantAfter, InstantIncomparable } +import std.optional { Present, Absent } +import std.algebra { Cons, Empty } +import extdeps.shell.exec { ProcessRunOutcome, ProcessRan, ProcessNotSpawned, ProcessObservation } +import extdeps.tools.findutils { find_regular_files_sha256_command, find_command_runs_another_program } +import gunbc.fabric.workspace_source { WorkspaceSourceRoot, workspace_source_fixed_roots, workspace_agent_state_slot_admission } +import gunbc.fabric.workspace_transcript_rate { + TranscriptFileStat, TranscriptFileMtime, TranscriptRateClockUndefined, workspace_transcript_rate_root_command, workspace_transcript_rate_root_reading, + TranscriptRootRead, TranscriptRootRefused, workspace_transcript_rate_interval, workspace_transcript_rate_mtime, + TranscriptRateSampled, TranscriptRateSampleRefused, TranscriptRateSample, workspace_transcript_rate_window_of, + TranscriptRateObserved, TranscriptRateRefused, TranscriptRateRootSelection, TranscriptRateObservation, workspace_transcript_rate_receipt, + workspace_transcript_rate_select_roots, workspace_transcript_rate_roots, TranscriptRateRootsSelected, TranscriptRateRootUndeclared, TranscriptRateSlotRefused, workspace_transcript_rate_fixed_root_labels, +} +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +// THE TRANSCRIPT APPEND-RATE INSTRUMENT AT ITS PURE BOUNDARIES, inputs supplied. Each claim has a +// discriminating red beside its positive control: the walk is a reader of metadata and the +// content-reading sha256 walk is not; a shrinking file is a truncation and never a negative append; +// an unreadable root refuses the sample and the window. The real walk over a real tree -- growth, +// creation, truncation and a missing root through find itself -- is the inhabitance claim in +// test.claim.workspace_transcript_rate_real_execution. + +data root: WorkspaceSourceRoot = WorkspaceSourceRoot { label: "claude-agent-state-test", path: "/r/.claude", build_output: [] } + +fn stat(member: String, size: Int, mtime: Int) -> TranscriptFileStat { + TranscriptFileStat { member: member as NonEmptyStr, size: byte_size(count: size), mtime: TranscriptFileMtime { since_epoch: nanosecond(count: mtime) } } +} + +// One clock for every sample of a window, as the wet sampler uses; a second process name or a CPU +// basis is a different clock. +fn at(ns: Int) -> ObservationInstant { + ObservationInstant { domain: ProcessMonotonic { process: "workspace-transcript-rate" }, basis: WallClock, since_origin: nanosecond(count: ns) } +} + +fn ran(code: Int, stdout: String, stderr: String) -> ProcessRunOutcome { + ProcessRan { observation: ProcessObservation { exit_code: code, stdout: stdout, stderr: stderr } } +} + +test fn the_walk_runs_no_other_program_while_the_sha256_walk_does() -> Bool { + let walk = workspace_transcript_rate_root_command(root: root) + (walk.program.invocation as String) == "find" + && find_command_runs_another_program(command: walk) == false + && find_command_runs_another_program(command: find_regular_files_sha256_command(root: "/r/.claude")) +} + +test fn a_shrinking_file_is_a_truncation_and_not_a_negative_append() -> Bool { + let row = workspace_transcript_rate_interval( + before: [stat(member: "r/.claude/projects/p/a.jsonl", size: 100, mtime: 1), stat(member: "r/.claude/projects/p/b.jsonl", size: 50, mtime: 1)], + after: [stat(member: "r/.claude/projects/p/a.jsonl", size: 40, mtime: 2), stat(member: "r/.claude/projects/p/b.jsonl", size: 60, mtime: 2)], + span: nanosecond(count: 10000000000), + ) + measure_count(m: row.bytes_appended) == 10 + && row.files_grown == 1 + && row.active_writers == 2 + && (match row.truncations { + Cons { head: t, tail: Empty } => (t.member as String) == "r/.claude/projects/p/a.jsonl" && measure_count(m: t.from) == 100 && measure_count(m: t.to) == 40 + Cons { head: _, tail: Cons { head: _, tail: _ } } => false + Empty => false + }) +} + +test fn growth_creation_removal_and_a_same_size_rewrite_are_counted_apart() -> Bool { + let row = workspace_transcript_rate_interval( + before: [stat(member: "a", size: 10, mtime: 1), stat(member: "b", size: 10, mtime: 1), stat(member: "c", size: 10, mtime: 1), stat(member: "d", size: 10, mtime: 1)], + after: [stat(member: "a", size: 15, mtime: 2), stat(member: "b", size: 10, mtime: 2), stat(member: "c", size: 10, mtime: 1), stat(member: "e", size: 7, mtime: 2)], + span: nanosecond(count: 100), + ) + measure_count(m: row.bytes_appended) == 12 + && row.files_grown == 1 && row.files_created == 1 && row.files_removed == 1 && row.files_rewritten_same_size == 1 + && row.active_writers == 3 && nanosecond_count(n: row.span) == 100 +} + +test fn the_walk_keeps_only_the_packs_transcript_class_and_reads_mtime_to_the_nanosecond() -> Bool { + match workspace_transcript_rate_root_reading(root: root, run: ran(code: 0, stdout: "64\t1728123456.1234567890\tprojects/p/s.jsonl\n9\t1728123456.5\tsettings.json\n5\t1.0\t.credentials.json\n3\t2.25\thistory.jsonl\n", stderr: "")) { + TranscriptRootRefused { root: _, detail: _ } => false + TranscriptRootRead { files: fs } => + length(fs) == 2 + && any(fs, f => (f.member as String) == "r/.claude/projects/p/s.jsonl" && measure_count(m: f.size) == 64 && nanosecond_count(n: f.mtime.since_epoch) == 1728123456123456789) + && any(fs, f => (f.member as String) == "r/.claude/history.jsonl" && nanosecond_count(n: f.mtime.since_epoch) == 2250000000) + } +} + +test fn an_unparsed_mtime_or_line_refuses_rather_than_dropping_the_file() -> Bool { + (match workspace_transcript_rate_mtime(text: "1.2.3") { Absent => true Present { value: _ } => false }) + && (match workspace_transcript_rate_root_reading(root: root, run: ran(code: 0, stdout: "64 projects/p/s.jsonl\n", stderr: "")) { + TranscriptRootRefused { root: _, detail: _ } => true + TranscriptRootRead { files: _ } => false + }) +} + +fn never(k: Int) -> TranscriptRateSample { + TranscriptRateSampleRefused { refusals: ["not sampled"] } +} + +test fn an_unreadable_root_refuses_the_sample_and_the_window() -> Bool { + let reading = workspace_transcript_rate_root_reading(root: root, run: ran(code: 1, stdout: "", stderr: "find: '/r/.claude': Permission denied")) + let not_spawned = workspace_transcript_rate_root_reading(root: root, run: ProcessNotSpawned { program: "find", cause: "no such program" }) + (match reading { TranscriptRootRefused { root: _, detail: _ } => true TranscriptRootRead { files: _ } => false }) + && (match not_spawned { TranscriptRootRefused { root: _, detail: _ } => true TranscriptRootRead { files: _ } => false }) + && (match workspace_transcript_rate_window_of(baseline: TranscriptRateSampleRefused { refusals: ["claude: denied"] }, remaining: 3, next: never, pause: k => true, rows: []) { + TranscriptRateRefused { reason: _, rows: rows } => length(rows) == 0 + TranscriptRateObserved { rate: _ } => false + TranscriptRateClockUndefined { from: _, to: _, order: _, rows: _ } => false + }) +} + +fn second_sample(k: Int) -> TranscriptRateSample { + if k == 2 { + TranscriptRateSampled { at: at(ns: 10000000000), files: [stat(member: "a", size: 1010, mtime: 2)] } + } else { + TranscriptRateSampled { at: at(ns: 20000000000), files: [stat(member: "a", size: 1030, mtime: 3)] } + } +} + +test fn a_completed_window_receipts_every_interval_and_the_summary_with_its_caveat() -> Bool { + match workspace_transcript_rate_window_of(baseline: TranscriptRateSampled { at: at(ns: 0), files: [stat(member: "a", size: 1000, mtime: 1)] }, remaining: 2, next: second_sample, pause: k => true, rows: []) { + TranscriptRateRefused { reason: _, rows: _ } => false + TranscriptRateClockUndefined { from: _, to: _, order: _, rows: _ } => false + TranscriptRateObserved { rate: r } => { + let receipt = workspace_transcript_rate_receipt(head: "", observation: TranscriptRateObserved { rate: r }) + length(r.rows) == 2 + && contains(receipt, "interval 1 span_ms=10000 bytes_appended=10 ") + && contains(receipt, "interval 2 span_ms=10000 bytes_appended=20 ") + && contains(receipt, " mean_bits_per_s=12 mean_bytes_per_s=1 ") + && contains(receipt, " p95_bits_per_s=16 p95_bytes_per_s=2 ") + && contains(receipt, "verdict=observed") + && contains(r.caveat as String, "ONE SAMPLE OF WORKLOAD") + } + } +} + +test fn a_failed_pause_refuses_the_window() -> Bool { + match workspace_transcript_rate_window_of(baseline: TranscriptRateSampled { at: at(ns: 0), files: [] }, remaining: 2, next: second_sample, pause: k => false, rows: []) { + TranscriptRateRefused { reason: _, rows: _ } => true + TranscriptRateObserved { rate: _ } => false + TranscriptRateClockUndefined { from: _, to: _, order: _, rows: _ } => false + } +} + +// The Claude root comes from the pack's slot admission, supplied here with an observed slot set; the +// Codex root is the pack's fixed row. An unknown slot refuses the selection. +fn roots_for(requested: String) -> TranscriptRateRootSelection { + workspace_transcript_rate_roots( + slot: workspace_agent_state_slot_admission(requested: Present { value: requested }, observed: ["gmail2", "gmail3"]), + fixed: workspace_transcript_rate_select_roots(labels: workspace_transcript_rate_fixed_root_labels, declared: workspace_source_fixed_roots), + ) +} + +test fn the_observed_roots_are_the_admitted_slot_and_the_codex_row_and_an_unknown_slot_refuses() -> Bool { + (match roots_for(requested: "gmail2") { + TranscriptRateRootsSelected { roots: rs } => length(rs) == 2 && any(rs, r => (r.path as String) == "/home/briansrls/.claude@gmail2") && any(rs, r => (r.path as String) == "/home/briansrls/.codex") + TranscriptRateRootUndeclared { label: _ } => false + TranscriptRateSlotRefused { slot: _ } => false + }) + && (match roots_for(requested: "gmail9") { + TranscriptRateSlotRefused { slot: _ } => true + TranscriptRateRootsSelected { roots: _ } => false + TranscriptRateRootUndeclared { label: _ } => false + }) + && (match workspace_transcript_rate_select_roots(labels: ["no-such-root"], declared: workspace_source_fixed_roots) { + TranscriptRateRootUndeclared { label: l } => (l as String) == "no-such-root" + TranscriptRateRootsSelected { roots: _ } => false + TranscriptRateSlotRefused { slot: _ } => false + }) +} + +fn sample_at(instant: ObservationInstant) -> fn(Int) -> TranscriptRateSample { + k => TranscriptRateSampled { at: instant, files: [] } +} + +fn window_after(instant: ObservationInstant) -> TranscriptRateObservation { + workspace_transcript_rate_window_of(baseline: TranscriptRateSampled { at: at(ns: 10), files: [] }, remaining: 1, next: sample_at(instant: instant), pause: k => true, rows: []) +} + +// THE SPAN IS DERIVED BY ORDERING TWO INSTANTS OF ONE CLOCK. The positive control is the ordered pair +// on the same domain and basis; the reds are a later instant that does not follow (InstantAfter) and +// an instant on another process's clock or another basis (InstantIncomparable) -- each its own typed +// refusal carrying the order, never a zero span read as a quiet interval. +test fn ordered_same_clock_instants_derive_a_span_and_reversed_or_foreign_instants_refuse_by_their_order() -> Bool { + (match window_after(instant: at(ns: 25)) { + TranscriptRateObserved { rate: r } => (match r.rows { Cons { head: row, tail: Empty } => nanosecond_count(n: row.span) == 15 Cons { head: _, tail: Cons { head: _, tail: _ } } => false Empty => false }) + TranscriptRateRefused { reason: _, rows: _ } => false + TranscriptRateClockUndefined { from: _, to: _, order: _, rows: _ } => false + }) + && (match window_after(instant: at(ns: 5)) { + TranscriptRateClockUndefined { from: _, to: _, order: o, rows: rows } => length(rows) == 0 && (match o { InstantAfter => true InstantIncomparable { left: _, right: _ } => false InstantBefore => false InstantSame => false }) + TranscriptRateObserved { rate: _ } => false + TranscriptRateRefused { reason: _, rows: _ } => false + }) + && (match window_after(instant: ObservationInstant { domain: ProcessMonotonic { process: "another-process" }, basis: WallClock, since_origin: nanosecond(count: 25) }) { + TranscriptRateClockUndefined { from: _, to: _, order: o, rows: _ } => (match o { InstantIncomparable { left: _, right: _ } => true InstantAfter => false InstantBefore => false InstantSame => false }) + TranscriptRateObserved { rate: _ } => false + TranscriptRateRefused { reason: _, rows: _ } => false + }) + && (match window_after(instant: ObservationInstant { domain: ProcessMonotonic { process: "workspace-transcript-rate" }, basis: CpuClock, since_origin: nanosecond(count: 25) }) { + TranscriptRateClockUndefined { from: _, to: _, order: o, rows: _ } => (match o { InstantIncomparable { left: _, right: _ } => true InstantAfter => false InstantBefore => false InstantSame => false }) + TranscriptRateObserved { rate: _ } => false + TranscriptRateRefused { reason: _, rows: _ } => false + }) +} diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index b5477ac289f..b51c255775c 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -13565,7 +13565,7 @@ fn emit_rust_first_method_call(receiver: Node, registry: Map, let skip_args = method_arg_nodes(texpr: receiver) let recv_str = emit_typed_expr(texpr: skip_recv, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info, fuel: 1024) let n_str = emit_typed_first_arg(args: skip_args, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) - concat(recv_str, ".iter().cloned().skip(", n_str, " as usize).next()") + concat(recv_str, ".get((", n_str, ") as usize).cloned()") } else { let recv_str = emit_typed_expr(texpr: receiver, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info, fuel: 1024) concat(recv_str, ".first().cloned()") diff --git a/src/v1/runtime_rust.dag b/src/v1/runtime_rust.dag index 5129153804f..df7ffedca8b 100644 --- a/src/v1/runtime_rust.dag +++ b/src/v1/runtime_rust.dag @@ -255,6 +255,118 @@ fn rt_collection_ops() -> String { "pub fn map_values(m: &HashMap) -> Vec { m.values().cloned().collect() }\n\n", "pub fn list_concat(mut a: Vec, b: Vec) -> Vec { a.append(b); a }\n\n", "pub fn list_push(mut list: Vec, item: T) -> Vec { list.push_back(item); list }\n\n", + "/// std.primitives skip_contract on the persistent carrier: the suffix after `n` members,\n", + "/// sharing the receiver's tree in O(log n) instead of copying the remainder, so a walk that\n", + "/// slices by offset is O(n log n) rather than quadratic. A negative `n` skips every member, as\n", + "/// the copying form's saturating `n as usize` did.\n", + "pub fn list_skip(items: &Vec, n: i64) -> Vec \{\n", + " let k = if n < 0 \{\n", + " items.len()\n", + " \} else \{\n", + " (n as usize).min(items.len())\n", + " \};\n", + " items.skip(k)\n", + "\}\n", + "\n", + "/// std.primitives take_contract on the persistent carrier: the first `n` members, sharing\n", + "/// the receiver's tree. A negative `n` keeps every member, as the copying form did.\n", + "pub fn list_take(items: &Vec, n: i64) -> Vec \{\n", + " let k = if n < 0 \{\n", + " items.len()\n", + " \} else \{\n", + " (n as usize).min(items.len())\n", + " \};\n", + " items.take(k)\n", + "\}\n", + "\n", + "#[cfg(test)]\n", + "mod list_slice_tests \{\n", + " use super::*;\n", + "\n", + " thread_local! \{\n", + " static CLONES: Cell = const \{ Cell::new(0) \};\n", + " \}\n", + "\n", + " /// A code point whose every copy is counted, so a slice's cost is read off the carrier's\n", + " /// own work rather than a clock.\n", + " #[derive(Debug, PartialEq)]\n", + " struct CountedCodePoint(u32);\n", + "\n", + " impl Clone for CountedCodePoint \{\n", + " fn clone(&self) -> Self \{\n", + " CLONES.with(|c| c.set(c.get() + 1));\n", + " CountedCodePoint(self.0)\n", + " \}\n", + " \}\n", + "\n", + " fn clones_during(f: impl FnOnce() -> R) -> (R, u64) \{\n", + " let before = CLONES.with(|c| c.get());\n", + " let r = f();\n", + " (r, CLONES.with(|c| c.get()) - before)\n", + " \}\n", + "\n", + " // Non-ASCII on purpose: emitted char_at / substring over a bare &str are O(offset) here,\n", + " // which is the walk the code-point slice replaces.\n", + " fn non_ascii_code_points(n: usize) -> Vec \{\n", + " [0xe9u32, 0x4e2d, 0x1f600, 0x61]\n", + " .iter()\n", + " .cycle()\n", + " .take(n)\n", + " .map(|cp| CountedCodePoint(*cp))\n", + " .collect()\n", + " \}\n", + "\n", + " const N: usize = 200_000;\n", + " // im's RRB split copies at most one boundary chunk per tree level; this is that bound with\n", + " // headroom, and three orders of magnitude below N.\n", + " const SLICE_COPY_BUDGET: u64 = 2_048;\n", + "\n", + " #[test]\n", + " fn a_slice_from_the_middle_copies_only_boundary_chunks() \{\n", + " let cps = Rc::new(non_ascii_code_points(N));\n", + " let (slice, copied) = clones_during(|| list_take(&list_skip(&cps, (N / 2) as i64), 7));\n", + " assert!(\n", + " copied <= SLICE_COPY_BUDGET,\n", + " \"skip+take copied \{\} members of \{\}\",\n", + " copied,\n", + " N\n", + " );\n", + " let expected: std::vec::Vec = cps.iter().skip(N / 2).take(7).map(|c| c.0).collect();\n", + " assert_eq!(\n", + " slice.iter().map(|c| c.0).collect::>(),\n", + " expected\n", + " );\n", + " \}\n", + "\n", + " // THE RED CONTROL: the extdeps.languages.rust.emit skip / take templates this runtime pair\n", + " // replaced, spelled as they emitted, on the same input and bound. It must exceed the budget,\n", + " // or the budget does not discriminate the quadratic.\n", + " #[test]\n", + " fn the_copying_template_form_exceeds_the_budget() \{\n", + " let cps = Rc::new(non_ascii_code_points(N));\n", + " let (_, copied) = clones_during(|| \{\n", + " let rest = cps.iter().cloned().skip(N / 2).collect::>();\n", + " rest.iter().cloned().take(7).collect::>()\n", + " \});\n", + " assert!(\n", + " copied > SLICE_COPY_BUDGET,\n", + " \"copying form copied only \{\}\",\n", + " copied\n", + " );\n", + " \}\n", + "\n", + " #[test]\n", + " fn negative_and_overlong_counts_keep_the_copying_forms_reading() \{\n", + " let xs: Vec = (0..10).collect();\n", + " for n in [-3i64, 0, 4, 10, 11, i64::MAX] \{\n", + " let copying_skip = xs.iter().cloned().skip(n as usize).collect::>();\n", + " let copying_take = xs.iter().cloned().take(n as usize).collect::>();\n", + " assert_eq!(list_skip(&xs, n), copying_skip, \"skip \{\}\", n);\n", + " assert_eq!(list_take(&xs, n), copying_take, \"take \{\}\", n);\n", + " \}\n", + " \}\n", + "\}\n", + "\n", "pub fn append(list: Rc>, item: T) -> Vec {\n", " let mut v = (*list).clone();\n", " v.push_back(item);\n", diff --git a/src/v1/stage0/src/extdeps_container_oci_digest.rs b/src/v1/stage0/src/extdeps_container_oci_digest.rs index 60344d27275..71788f3a266 100644 --- a/src/v1/stage0/src/extdeps_container_oci_digest.rs +++ b/src/v1/stage0/src/extdeps_container_oci_digest.rs @@ -259,7 +259,7 @@ pub fn oci_wire_digest_parts(raw: String) -> Option> { std::option::Option::None } else { match parts.clone().first().cloned() { - Some(algorithm) => match parts.clone().iter().cloned().skip(1 as usize).next() { + Some(algorithm) => match parts.clone().get((1) as usize).cloned() { Some(encoded) => Some(oci_wire_digest_parts_from_split( algorithm.clone(), encoded.clone(), diff --git a/src/v1/stage0/src/extdeps_languages_rust_emit.rs b/src/v1/stage0/src/extdeps_languages_rust_emit.rs index 0ec6134ed39..edcdd4fdc23 100644 --- a/src/v1/stage0/src/extdeps_languages_rust_emit.rs +++ b/src/v1/stage0/src/extdeps_languages_rust_emit.rs @@ -90,7 +90,7 @@ pub fn rust_container_templates() -> Rc> { pub fn rust_simple_method_specs() -> Rc>> { thread_local! { static CACHED: Rc>> = { - serde_json::from_str("[{\"method_name\": \"count\", \"template\": \"({recv}.len() as i64)\", \"wraps_result\": false}, {\"method_name\": \"join\", \"template\": \"{recv}.join(&{arg})\", \"wraps_result\": false}, {\"method_name\": \"split\", \"template\": \"{recv}.split(&{arg}).map(|s| s.to_string()).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"last\", \"template\": \"{recv}.last().cloned()\", \"wraps_result\": false}, {\"method_name\": \"first\", \"template\": \"{recv}.first().cloned()\", \"wraps_result\": false}, {\"method_name\": \"enumerate\", \"template\": \"{recv}.iter().cloned().enumerate().map(|(i, v)| (i as i64, v)).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"chars\", \"template\": \"{recv}.chars().map(|c| c as i64).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"skip\", \"template\": \"{recv}.iter().cloned().skip({arg} as usize).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"take\", \"template\": \"{recv}.iter().cloned().take({arg} as usize).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"is_empty\", \"template\": \"{recv}.is_empty()\", \"wraps_result\": false}]") + serde_json::from_str("[{\"method_name\": \"count\", \"template\": \"({recv}.len() as i64)\", \"wraps_result\": false}, {\"method_name\": \"join\", \"template\": \"{recv}.join(&{arg})\", \"wraps_result\": false}, {\"method_name\": \"split\", \"template\": \"{recv}.split(&{arg}).map(|s| s.to_string()).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"last\", \"template\": \"{recv}.last().cloned()\", \"wraps_result\": false}, {\"method_name\": \"first\", \"template\": \"{recv}.first().cloned()\", \"wraps_result\": false}, {\"method_name\": \"enumerate\", \"template\": \"{recv}.iter().cloned().enumerate().map(|(i, v)| (i as i64, v)).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"chars\", \"template\": \"{recv}.chars().map(|c| c as i64).collect::>()\", \"wraps_result\": true}, {\"method_name\": \"skip\", \"template\": \"v1_rt::list_skip(&{recv}, {arg})\", \"wraps_result\": true}, {\"method_name\": \"take\", \"template\": \"v1_rt::list_take(&{recv}, {arg})\", \"wraps_result\": true}, {\"method_name\": \"is_empty\", \"template\": \"{recv}.is_empty()\", \"wraps_result\": false}]") .expect("valid data definition") }; } diff --git a/src/v1/stage0/src/extdeps_uri.rs b/src/v1/stage0/src/extdeps_uri.rs index 303138dbac4..919c2a4bb70 100644 --- a/src/v1/stage0/src/extdeps_uri.rs +++ b/src/v1/stage0/src/extdeps_uri.rs @@ -118,18 +118,15 @@ pub fn uri_from_wire(url: String) -> Option> { ParsedHrefScheme::UnknownHref => std::option::Option::None, ParsedHrefScheme::HrefScheme { scheme: scheme, .. } => { let prefix = uri_scheme_wire(scheme.clone()); - let locator = Rc::new( - Rc::new( + let locator = Rc::new(v1_rt::list_skip( + &Rc::new( s.clone() .split(&prefix.clone()) .map(|s| s.to_string()) .collect::>(), - ) - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) + ), + 1, + )) .join(&prefix.clone()); if (v1_rt::starts_with(s.clone(), prefix.clone()) && !(locator.clone() == "".to_string())) diff --git a/src/v1/stage0/src/extdeps_uri_path.rs b/src/v1/stage0/src/extdeps_uri_path.rs index 1c6f7ff7508..83385bc9570 100644 --- a/src/v1/stage0/src/extdeps_uri_path.rs +++ b/src/v1/stage0/src/extdeps_uri_path.rs @@ -176,13 +176,7 @@ pub fn parse_segment_tokens(seg: String) -> Rc { }) } }; - let after_open = match before_and_rest - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + let after_open = match before_and_rest.clone().get((1) as usize).cloned() { Some(r) => r.clone(), std::option::Option::None => { return Rc::new(PathSegmentTokensResult::MalformedPathSegment { @@ -216,13 +210,7 @@ pub fn parse_segment_tokens(seg: String) -> Rc { }) } }; - let suffix = match name_and_suffix - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + let suffix = match name_and_suffix.clone().get((1) as usize).cloned() { Some(s) => s.clone(), std::option::Option::None => { return Rc::new(PathSegmentTokensResult::MalformedPathSegment { @@ -304,14 +292,7 @@ pub fn match_path_segments(path_only: String) -> Rc> { match raw_segs.clone().first().cloned() { Some(lead) => { if (lead.clone() == "".to_string()) { - Rc::new( - raw_segs - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) + Rc::new(v1_rt::list_skip(&raw_segs.clone(), 1)) } else { raw_segs.clone() } @@ -337,21 +318,8 @@ pub fn match_path_tokens( Some(tok) => match segs.clone().first().cloned() { std::option::Option::None => Rc::new(PathTemplateMatch::PathNotMatched), Some(seg) => match (*match_path_tokens( - Rc::new( - tokens - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), - Rc::new( - segs.clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&tokens.clone(), 1)), + Rc::new(v1_rt::list_skip(&segs.clone(), 1)), )) .clone() { @@ -461,51 +429,48 @@ pub fn parse_path_template(raw: String) -> Rc { tokens: first_tokens, .. } => { - let parsed = Rc::new( - segments - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - .fold( - Rc::new(PathTemplateParseResult::ParsedPathTemplate { - template: Rc::new(PathTemplate { - tokens: first_tokens.clone(), - }), - }), - |acc: Rc, seg: String| match (*acc.clone()).clone() - { - PathTemplateParseResult::MalformedPathTemplate { .. } => acc.clone(), - PathTemplateParseResult::ParsedPathTemplate { - template: path, .. - } => match (*parse_segment_tokens(seg.clone())).clone() { - PathSegmentTokensResult::MalformedPathSegment { - segment: s, - reason: r, - .. - } => Rc::new(PathTemplateParseResult::MalformedPathTemplate { - raw: raw.clone(), - segment: s.clone(), - reason: r.clone(), + let parsed = Rc::new(v1_rt::list_skip(&segments.clone(), 1)) + .iter() + .cloned() + .fold( + Rc::new(PathTemplateParseResult::ParsedPathTemplate { + template: Rc::new(PathTemplate { + tokens: first_tokens.clone(), }), - PathSegmentTokensResult::ParsedSegmentTokens { - tokens: seg_tokens, + }), + |acc: Rc, seg: String| match (*acc.clone()) + .clone() + { + PathTemplateParseResult::MalformedPathTemplate { .. } => { + acc.clone() + } + PathTemplateParseResult::ParsedPathTemplate { + template: path, .. - } => Rc::new(PathTemplateParseResult::ParsedPathTemplate { - template: Rc::new(PathTemplate { - tokens: v1_rt::concat( - path.tokens.clone(), - seg_tokens.clone(), - ), + } => match (*parse_segment_tokens(seg.clone())).clone() { + PathSegmentTokensResult::MalformedPathSegment { + segment: s, + reason: r, + .. + } => Rc::new(PathTemplateParseResult::MalformedPathTemplate { + raw: raw.clone(), + segment: s.clone(), + reason: r.clone(), }), - }), + PathSegmentTokensResult::ParsedSegmentTokens { + tokens: seg_tokens, + .. + } => Rc::new(PathTemplateParseResult::ParsedPathTemplate { + template: Rc::new(PathTemplate { + tokens: v1_rt::concat( + path.tokens.clone(), + seg_tokens.clone(), + ), + }), + }), + }, }, - }, - ); + ); parsed } }, @@ -520,10 +485,8 @@ pub fn uri_query_string(path: String) -> String { .map(|s| s.to_string()) .collect::>(), ) - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(q) => q.clone(), std::option::Option::None => "".to_string(), @@ -560,10 +523,8 @@ pub fn uri_query_param(path: String, key: String) -> String { .map(|s| s.to_string()) .collect::>(), ) - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(x) => x.clone(), std::option::Option::None => "".to_string(), diff --git a/src/v1/stage0/src/extdeps_version_semver.rs b/src/v1/stage0/src/extdeps_version_semver.rs index da6901c2ac6..43fd8151ef5 100644 --- a/src/v1/stage0/src/extdeps_version_semver.rs +++ b/src/v1/stage0/src/extdeps_version_semver.rs @@ -107,12 +107,8 @@ pub fn semver_compare_identifiers( Some(ah) => match b.clone().first().cloned() { Some(bh) => match semver_compare_identifier(ah.clone(), bh.clone()) { Ordering::Equal => { - let __tco_0 = Rc::new( - a.iter().cloned().skip(1 as usize).collect::>(), - ); - let __tco_1 = Rc::new( - b.iter().cloned().skip(1 as usize).collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&a, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&b, 1)); __tco_loop_a = __tco_0; __tco_loop_b = __tco_1; continue; @@ -217,13 +213,7 @@ pub fn semver_core_of_dotted(text: String) -> Option> { } pub fn semver_dotted_part(parts: Rc>, index: i64) -> Option { - match parts - .clone() - .iter() - .cloned() - .skip(index.clone() as usize) - .next() - { + match parts.clone().get((index.clone()) as usize).cloned() { std::option::Option::None => std::option::Option::None, Some(w) => match v1_rt::parse_int(w.clone()) { std::option::Option::None => std::option::Option::None, diff --git a/src/v1/stage0/src/std_graph.rs b/src/v1/stage0/src/std_graph.rs index a813e4db5eb..12b3c64d336 100644 --- a/src/v1/stage0/src/std_graph.rs +++ b/src/v1/stage0/src/std_graph.rs @@ -371,13 +371,7 @@ pub fn is_lexicographic_descent(mut __tco_loop_evidence: Rc break true; } DescentEvidence::NonIncreasing => { - let __tco_0 = Rc::new( - evidence - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&evidence, 1)); __tco_loop_evidence = __tco_0; continue; } diff --git a/src/v1/stage0/src/std_integer.rs b/src/v1/stage0/src/std_integer.rs index bbdca20220c..dea273c3614 100644 --- a/src/v1/stage0/src/std_integer.rs +++ b/src/v1/stage0/src/std_integer.rs @@ -129,13 +129,7 @@ pub fn first_uint8_out_of_range(mut __tco_loop_remaining: Rc>) -> Optio break Some(o.clone()); } UInt8Result::UInt8Ready { value: _, .. } => { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); __tco_loop_remaining = __tco_0; continue; } diff --git a/src/v1/stage0/src/std_occurrence_identity.rs b/src/v1/stage0/src/std_occurrence_identity.rs index 299c52ca17a..454b38f5d1b 100644 --- a/src/v1/stage0/src/std_occurrence_identity.rs +++ b/src/v1/stage0/src/std_occurrence_identity.rs @@ -549,14 +549,10 @@ pub fn occurrence_id_list_is_prefix_of( Some(head) => { if occurrence_id_eq(head.clone(), expected.clone()) { Rc::new(OccurrenceIdListPrefixAcc { - path_remaining: Rc::new( - acc.path_remaining - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + path_remaining: Rc::new(v1_rt::list_skip( + &acc.path_remaining.clone(), + 1, + )), ok: true, }) } else { diff --git a/src/v1/stage0/src/std_realization_schedule.rs b/src/v1/stage0/src/std_realization_schedule.rs index 452e02181d4..8f1847f187c 100644 --- a/src/v1/stage0/src/std_realization_schedule.rs +++ b/src/v1/stage0/src/std_realization_schedule.rs @@ -831,10 +831,8 @@ pub fn string_list_eq( break false; } else { { - let __tco_0 = - Rc::new(left.iter().cloned().skip(1 as usize).collect::>()); - let __tco_1 = - Rc::new(right.iter().cloned().skip(1 as usize).collect::>()); + let __tco_0 = Rc::new(v1_rt::list_skip(&left, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&right, 1)); __tco_loop_left = __tco_0; __tco_loop_right = __tco_1; continue; @@ -867,12 +865,8 @@ pub fn schedule_witness_entry_list_eq( break false; } else { { - let __tco_0 = Rc::new( - left.iter().cloned().skip(1 as usize).collect::>(), - ); - let __tco_1 = Rc::new( - right.iter().cloned().skip(1 as usize).collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&left, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&right, 1)); __tco_loop_left = __tco_0; __tco_loop_right = __tco_1; continue; @@ -980,12 +974,8 @@ pub fn runnable_batch_eq( break false; } else { { - let __tco_0 = Rc::new( - left.iter().cloned().skip(1 as usize).collect::>(), - ); - let __tco_1 = Rc::new( - right.iter().cloned().skip(1 as usize).collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&left, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&right, 1)); __tco_loop_left = __tco_0; __tco_loop_right = __tco_1; continue; @@ -1024,12 +1014,8 @@ pub fn schedule_eq(mut __tco_loop_left: Schedule, mut __tco_loop_right: Schedule break false; } else { { - let __tco_0 = Rc::new( - left.iter().cloned().skip(1 as usize).collect::>(), - ); - let __tco_1 = Rc::new( - right.iter().cloned().skip(1 as usize).collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&left, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&right, 1)); __tco_loop_left = __tco_0; __tco_loop_right = __tco_1; continue; diff --git a/src/v1/stage0/src/std_repair_input_origin.rs b/src/v1/stage0/src/std_repair_input_origin.rs index 283034fcc6e..e49ccbd854c 100644 --- a/src/v1/stage0/src/std_repair_input_origin.rs +++ b/src/v1/stage0/src/std_repair_input_origin.rs @@ -95,14 +95,10 @@ pub fn candidate_spelling_from_text(name: String) -> Rc { ); if ((segments.clone().len() as i64) > 1) { Rc::new(CandidateSpelling::QualifiedCandidate { - authored_qualifier: Rc::new( - segments - .clone() - .iter() - .cloned() - .take(v1_rt::int_sub((segments.clone().len() as i64), 1) as usize) - .collect::>(), - ) + authored_qualifier: Rc::new(v1_rt::list_take( + &segments.clone(), + v1_rt::int_sub((segments.clone().len() as i64), 1), + )) .join(&".".to_string()), member: match segments.clone().last().cloned() { Some(member) => member.clone(), diff --git a/src/v1/stage0/src/std_source_annotation.rs b/src/v1/stage0/src/std_source_annotation.rs index 64e9feefea5..687ac4e8be5 100644 --- a/src/v1/stage0/src/std_source_annotation.rs +++ b/src/v1/stage0/src/std_source_annotation.rs @@ -141,15 +141,7 @@ pub fn admit_annotations(result: Rc) -> Rc Rc::new(AnnotationAdmission::AnnotationsRefused { refusals: Rc::new(FreeSemigroup { head: head.clone(), - tail: Rc::new( - result - .refusals - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + tail: Rc::new(v1_rt::list_skip(&result.refusals.clone(), 1)), _phantom: std::marker::PhantomData, }), }), @@ -407,14 +399,7 @@ pub fn keyed_annotation_rows_agree( agreed: false, }), Some(other) => Rc::new(KeyedAnnotationRowWalk { - rest: Rc::new( - acc.rest - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + rest: Rc::new(v1_rt::list_skip(&acc.rest.clone(), 1)), agreed: ((acc.agreed.clone() && (row.subject_key.clone() == other.subject_key.clone())) && (row.text.clone() == other.text.clone())), diff --git a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs index c5c5b0b9a60..44905a9020b 100644 --- a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs +++ b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs @@ -271,7 +271,7 @@ pub fn first_or_empty(items: Rc>) -> String { } pub fn second_or_empty(items: Rc>) -> String { - match items.clone().iter().cloned().skip(1 as usize).next() { + match items.clone().get((1) as usize).cloned() { Some(v) => v.clone(), std::option::Option::None => "".to_string(), } diff --git a/src/v1/stage0/src/v1_compiler_complexity.rs b/src/v1/stage0/src/v1_compiler_complexity.rs index fcbdbcfd653..7d32976ba45 100644 --- a/src/v1/stage0/src/v1_compiler_complexity.rs +++ b/src/v1/stage0/src/v1_compiler_complexity.rs @@ -358,12 +358,7 @@ pub fn iteration_element_name( { let params = crate::v1_std_core::lambda_param_names_at(lambda.clone(), si.clone()); match method_callback_element_position(method_semantics.clone()) { - Some(pos) => params - .clone() - .iter() - .cloned() - .skip(pos.clone() as usize) - .next(), + Some(pos) => params.clone().get((pos.clone()) as usize).cloned(), std::option::Option::None => std::option::Option::None, } } @@ -710,14 +705,10 @@ pub fn parser_block_state_progress( match stmts.clone().last().cloned() { std::option::Option::None => DescentEvidence::DescentUnknown, Some(last_stmt) => { - let leading = Rc::new( - stmts - .clone() - .iter() - .cloned() - .take(v1_rt::int_sub((stmts.clone().len() as i64), 1) as usize) - .collect::>(), - ); + let leading = Rc::new(v1_rt::list_take( + &stmts.clone(), + v1_rt::int_sub((stmts.clone().len() as i64), 1), + )); let final_env = leading.iter().cloned().fold( env.clone(), |acc_env: Rc, stmt: Rc| match (*stmt.expr_data.clone()) @@ -1364,13 +1355,10 @@ pub fn parser_success_progress( } } ExprData::ExprBlock => { - let prefix = v1_rt::reverse(Rc::new( - v1_rt::reverse(expr.children.clone()) - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - )); + let prefix = v1_rt::reverse(Rc::new(v1_rt::list_skip( + &v1_rt::reverse(expr.children.clone()), + 1, + ))); let acc = prefix.iter().cloned().fold( Rc::new(ParserProgressAcc { edges: Rc::new(vec![]), @@ -1486,14 +1474,7 @@ pub fn infer_parser_always_advancing_members_worklist( let mut si = __tco_loop_si; match queue.clone().first().cloned() { Some(name) => { - let rest = Rc::new( - queue - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest = Rc::new(v1_rt::list_skip(&queue.clone(), 1)); if set_has(proven.clone(), name.clone()) { { let __tco_0 = rest.clone(); @@ -10220,10 +10201,8 @@ pub fn merge_param_evidence( DescentEvidence::Strict, |acc: DescentEvidence, call_evidence: Rc>>| match call_evidence .clone() - .iter() + .get((param_index.clone()) as usize) .cloned() - .skip(param_index.clone() as usize) - .next() { Some(rel) => crate::std_termination::descent_evidence_lattice_meet( acc.clone(), @@ -10246,10 +10225,8 @@ pub fn extract_shrink_factor( std::option::Option::None => std::option::Option::None, Some(prev) => match call_evidence .clone() - .iter() + .get((param_index.clone()) as usize) .cloned() - .skip(param_index.clone() as usize) - .next() { Some(rel) => match (*rel.clone()).clone() { SubValueRelation::StrictSubValue { factor: f, .. } => { @@ -10301,10 +10278,8 @@ pub fn max_path_descending( match de.clone() { Some(evidence) => match evidence .clone() - .iter() + .get((param_index.clone()) as usize) .cloned() - .skip(param_index.clone() as usize) - .next() { Some(rel) => match (*rel.clone()).clone() { SubValueRelation::StrictSubValue { .. } => 1, @@ -10420,10 +10395,8 @@ pub fn distinct_descended_fields( |acc: Rc>, call_evidence: Rc>>| { match call_evidence .clone() - .iter() + .get((param_index.clone()) as usize) .cloned() - .skip(param_index.clone() as usize) - .next() { Some(rel) => match (*rel.clone()).clone() { SubValueRelation::StrictSubValue { field: f, .. } => { diff --git a/src/v1/stage0/src/v1_compiler_emit.rs b/src/v1/stage0/src/v1_compiler_emit.rs index 7700517388f..02996da83ab 100644 --- a/src/v1/stage0/src/v1_compiler_emit.rs +++ b/src/v1/stage0/src/v1_compiler_emit.rs @@ -840,16 +840,12 @@ pub fn order_typed_call_args_from_semantics( for app in plan.iter().cloned() { __result.extend( (*match app.matched_argument_index.clone() { - Some(argument_index) => match args - .clone() - .iter() - .cloned() - .skip(argument_index.clone() as usize) - .next() - { - Some(arg) => Rc::new(vec![arg.clone()]), - std::option::Option::None => Rc::new(vec![]), - }, + Some(argument_index) => { + match args.clone().get((argument_index.clone()) as usize).cloned() { + Some(arg) => Rc::new(vec![arg.clone()]), + std::option::Option::None => Rc::new(vec![]), + } + } std::option::Option::None => Rc::new(vec![]), }) .iter() @@ -895,7 +891,7 @@ pub fn has_nested_records_node( source_indices.clone(), ); if is_map.clone() { - match n.children.clone().iter().cloned().skip(1 as usize).next() { + match n.children.clone().get((1) as usize).cloned() { Some(val_child) => { let __tco_0 = crate::v1_compiler_infer_types::child_type_node(val_child.clone()); @@ -1964,29 +1960,26 @@ pub fn render_node_type( } std::option::Option::None => "_".to_string(), }; - let second_child = - match n.children.clone().iter().cloned().skip(1 as usize).next() { - Some(c) => { - if (c.inferred.clone() != std::option::Option::None) { - render_node_type( - crate::v1_compiler_infer_types::resolved_type( - c.clone(), - ), - target.clone(), - shared_types.clone(), - source_indices.clone(), - ) - } else { - render_node_type( - c.clone(), - target.clone(), - shared_types.clone(), - source_indices.clone(), - ) - } + let second_child = match n.children.clone().get((1) as usize).cloned() { + Some(c) => { + if (c.inferred.clone() != std::option::Option::None) { + render_node_type( + crate::v1_compiler_infer_types::resolved_type(c.clone()), + target.clone(), + shared_types.clone(), + source_indices.clone(), + ) + } else { + render_node_type( + c.clone(), + target.clone(), + shared_types.clone(), + source_indices.clone(), + ) } - std::option::Option::None => "_".to_string(), - }; + } + std::option::Option::None => "_".to_string(), + }; let tuple_str = render_tuple_parts( Rc::new(vec![first_child.clone(), second_child.clone()]), target.clone(), @@ -2144,7 +2137,7 @@ pub fn render_node_type( ), std::option::Option::None => "_".to_string(), }; - let v = match n.children.clone().iter().cloned().skip(1 as usize).next() { + let v = match n.children.clone().get((1) as usize).cloned() { Some(vn) => render_node_type( crate::v1_compiler_infer_types::child_type_node(vn.clone()), target.clone(), @@ -2271,7 +2264,7 @@ pub fn render_tuple_parts(parts: Rc>, target: RenderTarget) -> Strin if ((parts.clone().len() as i64) > 0) { if ((parts.clone().len() as i64) == 2) { match parts.clone().first().cloned() { - Some(p0) => match parts.clone().iter().cloned().skip(1 as usize).next() { + Some(p0) => match parts.clone().get((1) as usize).cloned() { Some(p1) => crate::v1_compiler_emit_core_support::apply_type_template2( ts.pair_template.clone(), p0.clone(), @@ -3474,14 +3467,10 @@ pub fn block_stmts_init(stmts: Rc>>) -> Rc>> { if ((stmts.clone().len() as i64) <= 1) { Rc::new(vec![]) } else { - Rc::new( - stmts - .clone() - .iter() - .cloned() - .take(v1_rt::int_sub((stmts.clone().len() as i64), 1) as usize) - .collect::>(), - ) + Rc::new(v1_rt::list_take( + &stmts.clone(), + v1_rt::int_sub((stmts.clone().len() as i64), 1), + )) } } @@ -6816,13 +6805,7 @@ pub fn emit_block_stmts_shared( }; let next_scope = scope_after_expr(stmt.clone(), scope.clone()); { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); let __tco_1 = v1_rt::rc_list_push(text, line.clone()); let __tco_2 = next_scope.clone(); let __tco_3 = depth; @@ -6870,14 +6853,7 @@ pub fn emit_init_block_stmts_shared( }); } Some(stmt) => { - let rest = Rc::new( - remaining - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest = Rc::new(v1_rt::list_skip(&remaining.clone(), 1)); match rest.clone().first().cloned() { std::option::Option::None => { break Rc::new(BlockEmitState { @@ -8620,13 +8596,7 @@ pub fn emit_typed_tco_reassign_shared( let mut __result = Vec::new(); for pair in pairs.iter().cloned() { __result.push({ - let av = match arg_values - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { + let av = match arg_values.clone().get((pair.0.clone()) as usize).cloned() { Some(v) => v.clone(), std::option::Option::None => pair.1.clone(), }; diff --git a/src/v1/stage0/src/v1_compiler_emit_core_support.rs b/src/v1/stage0/src/v1_compiler_emit_core_support.rs index cd3986c091a..251e9b4f561 100644 --- a/src/v1/stage0/src/v1_compiler_emit_core_support.rs +++ b/src/v1/stage0/src/v1_compiler_emit_core_support.rs @@ -552,13 +552,7 @@ pub fn apply_named_template_nested( match keys.clone().first().cloned() { std::option::Option::None => template.clone(), Some(key) => { - let rest = Rc::new( - keys.clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest = Rc::new(v1_rt::list_skip(&keys.clone(), 1)); let placeholder = v1_rt::concat(v1_rt::concat("{".to_string(), key.clone()), "}".to_string()); match v1_rt::map_get(&bindings, key.clone()) { diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index e28b0327929..cc6400b2ee9 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -688,14 +688,7 @@ pub fn render_rust_type_without_applied_binding( let key_node = crate::v1_compiler_infer_types::child_type_node( key_child.clone(), ); - let val_node = match n - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + let val_node = match n.children.clone().get((1) as usize).cloned() { Some(val_child) => { if rust_type_node_is_arrow(val_child.clone()) { val_child.clone() @@ -2880,7 +2873,7 @@ if peel.clone() { } else { if crate::v1_compiler_infer_types::node_is_keyed_collection(n.clone(), source_indices.clone()) { match arg_list.clone().first().cloned() { - Some(k) => match arg_list.clone().iter().cloned().skip(1 as usize).next() { + Some(k) => match arg_list.clone().get((1) as usize).cloned() { Some(v) => crate::v1_compiler_emit::emit_keyed_container_type(name.clone(), k.clone(), v.clone(), base.clone(), RenderTarget::Rust), std::option::Option::None => v1_rt::concat(v1_rt::concat(v1_rt::concat(base.clone(), "<".to_string()), arg_list.clone().join(&", ".to_string())), ">".to_string()), }, @@ -3295,7 +3288,7 @@ pub fn rust_alias_rhs_applied_container_or_base( ) -> String { if crate::v1_compiler_infer_types::node_is_keyed_collection(n.clone(), source_indices.clone()) { match arg_list.clone().first().cloned() { - Some(k) => match arg_list.clone().iter().cloned().skip(1 as usize).next() { + Some(k) => match arg_list.clone().get((1) as usize).cloned() { Some(v) => crate::v1_compiler_emit::emit_keyed_container_type( leaf.clone(), k.clone(), @@ -5951,13 +5944,7 @@ pub fn emit_rust_block_stmts( let next_scope = crate::v1_compiler_emit::scope_after_expr(stmt.clone(), scope.clone()); { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); let __tco_1 = v1_rt::rc_list_push(text, line.clone()); let __tco_2 = next_scope.clone(); let __tco_3 = registry; @@ -6010,14 +5997,7 @@ pub fn emit_rust_init_block_stmts( }); } Some(stmt) => { - let rest = Rc::new( - remaining - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest = Rc::new(v1_rt::list_skip(&remaining.clone(), 1)); match rest.clone().first().cloned() { std::option::Option::None => { break Rc::new(BlockEmitState { @@ -10589,16 +10569,10 @@ pub fn qualified_type_reference_rows( .map(|s| s.to_string()) .collect::>(), ); - let qualifier = Rc::new( - segments - .clone() - .iter() - .cloned() - .take( - v1_rt::int_sub((segments.clone().len() as i64), 1) as usize - ) - .collect::>(), - ) + let qualifier = Rc::new(v1_rt::list_take( + &segments.clone(), + v1_rt::int_sub((segments.clone().len() as i64), 1), + )) .join(&".".to_string()); if ((((qualifier.clone() == this_module_name.clone()) || crate::v1_compiler_infer_types::emit_map_has( @@ -10796,8 +10770,8 @@ pub fn qualified_type_reference_use_lines( .cloned() { __result.extend( - (*Rc::new( - Rc::new({ + (*Rc::new(v1_rt::list_take( + &Rc::new({ let mut __result = Vec::new(); for l in typed.iter().cloned() { if (l.text.clone() == text.clone()) { @@ -10805,12 +10779,9 @@ pub fn qualified_type_reference_use_lines( } } __result - }) - .iter() - .cloned() - .take(1 as usize) - .collect::>(), - )) + }), + 1, + ))) .iter() .cloned(), ); @@ -14667,10 +14638,8 @@ pub fn rust_use_after_crate(line: String) -> String { .map(|s| s.to_string()) .collect::>(), ) - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(rest) => rest.clone(), std::option::Option::None => "".to_string(), @@ -14815,10 +14784,8 @@ pub fn rust_pub_use_braced_names(line: String) -> Rc> { .map(|s| s.to_string()) .collect::>(), ) - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(rest) => match Rc::new( rest.clone() @@ -15014,10 +14981,8 @@ pub fn rust_use_bound_symbol(entry: String) -> String { .map(|s| s.to_string()) .collect::>(), ) - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(bound_name) => bound_name.clone(), std::option::Option::None => entry.clone(), @@ -19013,20 +18978,8 @@ pub fn v1_record_decl_slot_arg_name( ); } else { { - let __tco_0 = Rc::new( - decl_params - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); - let __tco_1 = Rc::new( - type_args - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&decl_params, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&type_args, 1)); let __tco_2 = slot_name; let __tco_3 = source_indices; __tco_loop_decl_params = __tco_0; @@ -19100,22 +19053,8 @@ pub fn v1_positional_bound_forwarded_names( v1_rt::concat( here.clone(), v1_positional_bound_forwarded_names( - Rc::new( - callee_names - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), - Rc::new( - wrapper_names - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&callee_names.clone(), 1)), + Rc::new(v1_rt::list_skip(&wrapper_names.clone(), 1)), callee_bound_param_names.clone(), wrapper_generic_param_names.clone(), ), @@ -24775,14 +24714,7 @@ pub fn rust_empty_map_value_type_str( shared_types: Rc>, source_indices: Rc>>, ) -> String { - match map_type - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + match map_type.children.clone().get((1) as usize).cloned() { Some(value_child) => { let rendered = render_rust_type( crate::v1_compiler_infer_types::child_type_node(value_child.clone()), @@ -26542,10 +26474,8 @@ pub fn rust_call_arg_fail_closed_unwrap( ) -> String { match callee.clone() { Some(info) => match function_value_params(info.params.clone()) - .iter() + .get((idx.clone()) as usize) .cloned() - .skip(idx.clone() as usize) - .next() { Some(param) => { let param_type = crate::v1_std_core::param_node_type_expr(param.clone()); @@ -26589,10 +26519,8 @@ pub fn rust_call_arg_function_value_adapt( } match callee.clone() { Some(info) => match function_value_params(info.params.clone()) - .iter() + .get((idx.clone()) as usize) .cloned() - .skip(idx.clone() as usize) - .next() { Some(param) => { let arity = (crate::v1_std_core::param_node_type_expr(param.clone()) @@ -26702,14 +26630,7 @@ pub fn lambda_argument_scope(arg: Rc, scope: Rc) -> Rc>(), - ), + Rc::new(v1_rt::list_skip(&arg.children.clone(), 1)), ), _ => scope.clone(), } @@ -26750,7 +26671,7 @@ pub fn emit_typed_call( scope.clone(), ); let get_list = get_args.clone().first().cloned(); - let get_idx = get_args.clone().iter().cloned().skip(1 as usize).next(); + let get_idx = get_args.clone().get((1) as usize).cloned(); let get_result = match get_list.clone() { Some(list_arg) => match get_idx.clone() { Some(idx_arg) => { @@ -26814,10 +26735,8 @@ pub fn emit_typed_call( let update_arg = crate::v1_std_core::arg_value( with_args .clone() - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() .clone() .unwrap(), ); @@ -27054,10 +26973,8 @@ pub fn emit_typed_call( let a = pair.1.clone(); let arg_emit_info = match application_plan .clone() - .iter() + .get((idx.clone()) as usize) .cloned() - .skip(idx.clone() as usize) - .next() { Some(application) => { crate::v1_compiler_infer_emit_info::emit_info_with_expected_type( @@ -27511,13 +27428,7 @@ pub fn emit_nested_rt_concat( ) }; { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); let __tco_1 = next_acc.clone(); let __tco_2 = shared_types; __tco_loop_remaining = __tco_0; @@ -27892,13 +27803,7 @@ pub fn lambda_scope_from_children( .fold(scope.clone(), |acc: Rc, pair: (i64, String)| { let idx = pair.0.clone(); let param_name = pair.1.clone(); - let param_type = match param_nodes - .clone() - .iter() - .cloned() - .skip(idx.clone() as usize) - .next() - { + let param_type = match param_nodes.clone().get((idx.clone()) as usize).cloned() { Some(pn) => match pn.inferred.clone().as_deref().cloned() { Some(InferredNode::Resolved { node: resolved_type, @@ -27947,13 +27852,7 @@ pub fn lambda_param_type_strs( let inferred_type = if (fold_acc_uses_fallback.clone() && (idx.clone() == 0)) { std::option::Option::None } else { - match param_nodes - .clone() - .iter() - .cloned() - .skip(idx.clone() as usize) - .next() - { + match param_nodes.clone().get((idx.clone()) as usize).cloned() { Some(pn) => match pn.inferred.clone().as_deref().cloned() { Some(InferredNode::Resolved { node: param_type, .. @@ -28008,16 +27907,11 @@ pub fn lambda_param_type_strs( std::option::Option::None => std::option::Option::None, } }; - let fallback_type = match fallback_types - .clone() - .iter() - .cloned() - .skip(idx.clone() as usize) - .next() - { - Some(ty) => ty.clone(), - std::option::Option::None => "_".to_string(), - }; + let fallback_type = + match fallback_types.clone().get((idx.clone()) as usize).cloned() { + Some(ty) => ty.clone(), + std::option::Option::None => "_".to_string(), + }; let spec = crate::v1_compiler_emit_core_support::language_spec(RenderTarget::Rust); let ident = crate::v1_compiler_emit::emit_ident(param_name.clone(), RenderTarget::Rust); @@ -28057,15 +27951,7 @@ pub fn emit_typed_collection_lambda( scope.type_env.clone().source_indices.clone(), ); let bd = crate::v1_std_core::lambda_body(lambda_expr.clone()); - let pn = Rc::new( - lambda_expr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let pn = Rc::new(v1_rt::list_skip(&lambda_expr.children.clone(), 1)); let param_strs = lambda_param_type_strs( ps.clone(), pn.clone(), @@ -28136,15 +28022,7 @@ pub fn emit_typed_fold_lambda( scope.type_env.clone().source_indices.clone(), ); let bd = crate::v1_std_core::lambda_body(lambda_expr.clone()); - let pn = Rc::new( - lambda_expr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let pn = Rc::new(v1_rt::list_skip(&lambda_expr.children.clone(), 1)); let safe_acc_type = rust_fold_safe_acc_type(acc_type_str.clone()); let fallback_types = Rc::new({ let mut __result = Vec::new(); @@ -28406,7 +28284,7 @@ pub fn emit_rust_fold_method_call( scope.type_env.clone().source_indices.clone(), acc_type_node.clone(), ); - let fold_lambda_node = match args.clone().iter().cloned().skip(1 as usize).next() { + let fold_lambda_node = match args.clone().get((1) as usize).cloned() { Some(a) => crate::v1_std_core::arg_value(a.clone()), std::option::Option::None => type_variable_node("".to_string()), }; @@ -28700,7 +28578,7 @@ pub fn emit_rust_fold_method_call( let sharing = crate::v1_compiler_emit_core_support::language_spec(RenderTarget::Rust) .sharing .clone(); - let elem_unused = match args.clone().iter().cloned().skip(1 as usize).next() { + let elem_unused = match args.clone().get((1) as usize).cloned() { Some(a) => fold_lambda_element_unused( crate::v1_std_core::arg_value(a.clone()), scope.type_env.clone().source_indices.clone(), @@ -28720,7 +28598,7 @@ pub fn emit_rust_fold_method_call( iter_template.clone(), recv_str.clone(), ); - let step_awaits = match args.clone().iter().cloned().skip(1 as usize).next() { + let step_awaits = match args.clone().get((1) as usize).cloned() { Some(a) => rust_expr_reaches_awaited_call( crate::v1_std_core::arg_value(a.clone()), registry.clone(), @@ -28742,7 +28620,7 @@ pub fn emit_rust_fold_method_call( ) } else { { - let fold_fn = match args.clone().iter().cloned().skip(1 as usize).next() { + let fold_fn = match args.clone().get((1) as usize).cloned() { Some(a) => emit_typed_fold_lambda( crate::v1_std_core::arg_value(a.clone()), lambda_acc_type_str.clone(), @@ -28791,13 +28669,13 @@ pub fn emit_rust_effectful_fold_loop( ExprData::ExprLambda => { let si = scope.type_env.clone().source_indices.clone(); let ps = crate::v1_std_core::lambda_param_names_at(lambda_expr.clone(), si.clone()); -let pn = Rc::new(lambda_expr.children.clone().iter().cloned().skip(1 as usize).collect::>()); +let pn = Rc::new(v1_rt::list_skip(&lambda_expr.children.clone(), 1)); let acc_param_type = rust_fold_safe_acc_type(acc_type_str.clone()); let acc_name = match ps.clone().first().cloned() { Some(n) => n.clone(), std::option::Option::None => "_".to_string(), }; -let elem_name = match ps.clone().iter().cloned().skip(1 as usize).next() { +let elem_name = match ps.clone().get((1) as usize).cloned() { Some(n) => n.clone(), std::option::Option::None => "_".to_string(), }; @@ -28898,7 +28776,7 @@ pub fn fold_lambda_element_unused( lambda_expr.clone(), source_indices.clone(), ); - match ps.clone().iter().cloned().skip(1 as usize).next() { + match ps.clone().get((1) as usize).cloned() { Some(elem_name) => (elem_name.clone() == "_".to_string()), std::option::Option::None => false, } @@ -29111,15 +28989,10 @@ pub fn emit_rust_map_method_call( let lambda_scope = lambda_scope_from_children( scope.clone(), ps.clone(), - Rc::new( - crate::v1_std_core::arg_value(a.clone()) - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip( + &crate::v1_std_core::arg_value(a.clone()).children.clone(), + 1, + )), ); let body_str = emit_typed_expr( bd.clone(), @@ -29210,15 +29083,10 @@ pub fn emit_rust_map_method_call( let lambda_scope = lambda_scope_from_children( scope.clone(), ps.clone(), - Rc::new( - crate::v1_std_core::arg_value(a.clone()) - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip( + &crate::v1_std_core::arg_value(a.clone()).children.clone(), + 1, + )), ); let body_str = emit_typed_expr( bd.clone(), @@ -29334,15 +29202,10 @@ pub fn emit_rust_higher_order_method( let lambda_scope = lambda_scope_from_children( scope.clone(), ps.clone(), - Rc::new( - crate::v1_std_core::arg_value(a.clone()) - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip( + &crate::v1_std_core::arg_value(a.clone()).children.clone(), + 1, + )), ); let body_str = emit_typed_expr( bd.clone(), @@ -29654,10 +29517,10 @@ pub fn emit_rust_first_method_call( ); v1_rt::concat( v1_rt::concat( - v1_rt::concat(recv_str.clone(), ".iter().cloned().skip(".to_string()), + v1_rt::concat(recv_str.clone(), ".get((".to_string()), n_str.clone(), ), - " as usize).next()".to_string(), + ") as usize).cloned()".to_string(), ) } } else { @@ -35487,14 +35350,7 @@ pub fn emit_tco_init_block_stmts( }); } Some(stmt) => { - let rest = Rc::new( - remaining - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest = Rc::new(v1_rt::list_skip(&remaining.clone(), 1)); match rest.clone().first().cloned() { std::option::Option::None => { break Rc::new(BlockEmitState { @@ -36679,13 +36535,7 @@ pub fn emit_typed_tco_reassign( let mut __result = Vec::new(); for pair in pairs.iter().cloned() { __result.push({ - let av = match arg_values - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { + let av = match arg_values.clone().get((pair.0.clone()) as usize).cloned() { Some(v) => v.clone(), std::option::Option::None => pair.1.clone(), }; @@ -39003,16 +38853,7 @@ pub fn emit_shell_call( let arg_lines = if ((argv.clone().len() as i64) > 1) { Rc::new({ let mut __result = Vec::new(); - for arg in Rc::new( - argv.clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - { + for arg in Rc::new(v1_rt::list_skip(&argv.clone(), 1)).iter().cloned() { __result.push( if shell_argv_element_is_word_list( arg.clone(), diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 1b84f5114e5..de484c14aad 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -481,13 +481,7 @@ pub fn infer_block_stmts( let next_scope = scope_after_stmt_node(stmt.clone(), stmt_rt.clone(), scope.clone()); { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); let __tco_1 = v1_rt::int_sub(remaining_count, 1); let __tco_2 = next_scope.clone(); let __tco_3 = v1_rt::rc_list_push(typed_stmts, stmt_typed.clone()); @@ -638,13 +632,7 @@ Rc::new(InferScopeComponents { } }); { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); let __tco_1 = parent_index; let __tco_2 = env; let __tco_3 = parent_result.svc_registry.clone(); @@ -658,13 +646,7 @@ Rc::new(InferScopeComponents { } } std::option::Option::None => { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); let __tco_1 = parent_index; let __tco_2 = env; let __tco_3 = svc_registry; @@ -737,10 +719,8 @@ pub fn classify_field_recursion( let value_type = match type_expr .children .clone() - .iter() + .get((value_index.clone()) as usize) .cloned() - .skip(value_index.clone() as usize) - .next() { Some(t) => { crate::v1_std_core::authored_name_at(source_indices.clone(), t.clone()) @@ -2891,7 +2871,7 @@ pub fn record_lit_instantiated_fields( Some(template_fields) => { let subst = Rc::new(decl.params.clone().iter().cloned().enumerate().map(|(i, v)| (i as i64, v)).collect::>()).iter().cloned().fold(v1_rt::rc_empty_map::>(), |acc: Rc>>, pair: (i64, Rc)| { let slot = crate::v1_std_core::authored_name_at(si.clone(), pair.1.clone()); -match exp.children.clone().iter().cloned().skip(pair.0.clone() as usize).next() { +match exp.children.clone().get((pair.0.clone()) as usize).cloned() { Some(arg) => v1_rt::rc_map_insert(acc.clone(), slot.clone(), crate::v1_compiler_infer_types::child_type_node(arg.clone())), std::option::Option::None => acc.clone(), } @@ -4414,16 +4394,9 @@ pub fn list_literal_optional_member_diags( let head_type = crate::v1_compiler_infer_types::resolved_type(head.clone()); Rc::new({ let mut __result = Vec::new(); - for te in Rc::new( - typed_elements - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() + for te in Rc::new(v1_rt::list_skip(&typed_elements.clone(), 1)) + .iter() + .cloned() { __result.extend( (*optional_at_required_obligation_diags( @@ -6926,20 +6899,8 @@ pub fn coproduct_applied_type_arguments_conflict_scan( break true; } else { { - let __tco_0 = Rc::new( - declared_args - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); - let __tco_1 = Rc::new( - produced_args - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&declared_args, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&produced_args, 1)); let __tco_2 = scope; __tco_loop_declared_args = __tco_0; __tco_loop_produced_args = __tco_1; @@ -7079,20 +7040,8 @@ pub fn applied_type_arguments_conflict_scan( break true; } else { { - let __tco_0 = Rc::new( - declared_args - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); - let __tco_1 = Rc::new( - produced_args - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&declared_args, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&produced_args, 1)); let __tco_2 = scope; __tco_loop_declared_args = __tco_0; __tco_loop_produced_args = __tco_1; @@ -7970,14 +7919,14 @@ pub fn call_argument_formal_at_position( } __result }) - .iter() + .get( + (call_argument_positional_rank( + arguments.clone(), + argument_index.clone(), + source_indices.clone(), + )) as usize, + ) .cloned() - .skip(call_argument_positional_rank( - arguments.clone(), - argument_index.clone(), - source_indices.clone(), - ) as usize) - .next() { Some(positional) => Rc::new(CallArgumentFormalSelection::CallArgumentFormalSelected { formal_index: positional.0.clone(), @@ -8106,13 +8055,7 @@ pub fn build_call_application_plan( .cloned() { __result.extend( - (*match formals - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { + (*match formals.clone().get((pair.0.clone()) as usize).cloned() { Some(carried) => Rc::new(vec![Rc::new(ResolvedCallFormal { formal_index: pair.0.clone(), formal: Rc::new(ResolvedFormal { @@ -8190,7 +8133,7 @@ pub fn direct_call_structured_application_mismatch_diags( let formal_subst = app.formal.clone().declared_type.clone(); let formal = app.formal.clone().declaration_bound_conformance.clone(); match app.matched_argument_index.clone() { - Some(arg_index) => match typed_args.clone().iter().cloned().skip(arg_index.clone() as usize).next() { + Some(arg_index) => match typed_args.clone().get((arg_index.clone()) as usize).cloned() { Some(ta) => { let actual_expr = crate::v1_std_core::arg_value(ta.clone()); let host_into_sequence = (!type_node_is_callable(formal.clone()) && crate::v1_compiler_infer_env::host_text_into_structural_sequence(formal.clone(), crate::v1_compiler_infer_resolve::peel_nominal_alias_identity(crate::v1_compiler_infer_types::resolved_type(actual_expr.clone()), type_env.clone(), module_name.clone()), crate::v1_compiler_infer_types::resolved_type(actual_expr.clone()), type_env.clone())); @@ -8274,7 +8217,7 @@ pub fn sibling_effect_order_diags( Some(x) => x.clone(), std::option::Option::None => "".to_string(), }; - let b = match effectful.clone().iter().cloned().skip(1 as usize).next() { + let b = match effectful.clone().get((1) as usize).cloned() { Some(x) => x.clone(), std::option::Option::None => "".to_string(), }; @@ -8338,10 +8281,8 @@ pub fn direct_call_argument_inhabitance_diags( (*match app.matched_argument_index.clone() { Some(arg_index) => match typed_args .clone() - .iter() + .get((arg_index.clone()) as usize) .cloned() - .skip(arg_index.clone() as usize) - .next() { Some(ta) => { if type_node_is_callable(app.formal.clone().declared_type.clone()) { @@ -8395,13 +8336,13 @@ pub fn direct_call_generic_type_argument_inhabitance_diags( let mut __result = Vec::new(); for app in plan.iter().cloned() { __result.extend((*match app.matched_argument_index.clone() { - Some(arg_index) => match typed_args.clone().iter().cloned().skip(arg_index.clone() as usize).next() { + Some(arg_index) => match typed_args.clone().get((arg_index.clone()) as usize).cloned() { Some(ta) => { let actual_expr = crate::v1_std_core::arg_value(ta.clone()); let actual = crate::v1_compiler_infer_types::resolved_type(actual_expr.clone()); if ((((exposure_is_application(exposure_view_for_node(app.formal.clone().declared_type.clone(), source_indices.clone())) && exposure_is_application(exposure_view_for_node(actual.clone(), source_indices.clone()))) && ((app.formal.clone().declared_type.clone().children.clone().len() as i64) > 0)) && ((app.formal.clone().declared_type.clone().children.clone().len() as i64) == (actual.children.clone().len() as i64))) && (crate::v1_std_core::authored_name_at(source_indices.clone(), app.formal.clone().declared_type.clone()) == crate::v1_std_core::authored_name_at(source_indices.clone(), actual.clone()))) { - Rc::new({ let mut __result = Vec::new(); for declared_pair in Rc::new(app.formal.clone().declared_type.clone().children.clone().iter().cloned().enumerate().map(|(i, v)| (i as i64, v)).collect::>()).iter().cloned() { __result.extend((*match app.formal.clone().substitution_basis.clone().children.clone().iter().cloned().skip(declared_pair.0.clone() as usize).next() { - Some(raw_declared_child) => match actual.children.clone().iter().cloned().skip(declared_pair.0.clone() as usize).next() { + Rc::new({ let mut __result = Vec::new(); for declared_pair in Rc::new(app.formal.clone().declared_type.clone().children.clone().iter().cloned().enumerate().map(|(i, v)| (i as i64, v)).collect::>()).iter().cloned() { __result.extend((*match app.formal.clone().substitution_basis.clone().children.clone().get((declared_pair.0.clone()) as usize).cloned() { + Some(raw_declared_child) => match actual.children.clone().get((declared_pair.0.clone()) as usize).cloned() { Some(produced_child) => if (applied_type_argument_identity_known(crate::v1_std_core::authored_name_at(source_indices.clone(), crate::v1_compiler_infer_types::child_type_node(raw_declared_child.clone())), scope.clone()) && applied_type_argument_identity_known(crate::v1_std_core::authored_name_at(source_indices.clone(), crate::v1_compiler_infer_types::child_type_node(produced_child.clone())), scope.clone())) { declared_type_obligation_diags(Rc::new(DeclaredTypeObligation { position: DeclaredTypePosition::PositionGenericTypeArgument, @@ -8615,10 +8556,8 @@ pub fn callable_signature_substitution( acc.clone(), |st: Rc>>, pair: (i64, Rc)| match a_params .clone() - .iter() + .get((pair.0.clone()) as usize) .cloned() - .skip(pair.0.clone() as usize) - .next() { Some(ap) => callable_leaf_substitution( callable_param_type(pair.1.clone()), @@ -8739,13 +8678,7 @@ pub fn callable_arrow_mismatch( .iter() .cloned() { - if match a_params - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { + if match a_params.clone().get((pair.0.clone()) as usize).cloned() { Some(ap) => callable_component_ground_mismatch( callable_param_type(pair.1.clone()), callable_param_type(ap.clone()), @@ -8830,16 +8763,9 @@ pub fn expression_value_type(e: Rc) -> Rc { resolved_callable_type( Rc::new({ let mut __result = Vec::new(); - for lp in Rc::new( - e.children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() + for lp in Rc::new(v1_rt::list_skip(&e.children.clone(), 1)) + .iter() + .cloned() { __result.push(lambda_arrow_param(lp.clone())); } @@ -9068,12 +8994,9 @@ pub fn borrowed_callable_call_type( .cloned() { Some(named_ta) => Some(named_ta.clone()), - std::option::Option::None => typed_args - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next(), + std::option::Option::None => { + typed_args.clone().get((pair.0.clone()) as usize).cloned() + } }; match matched_arg.clone() { Some(ta) => unify_generics( @@ -9158,14 +9081,10 @@ pub fn call_arg_bound_param_at( source_indices: Rc>>, positionally_eligible_names: Rc>, ) -> Option { - Rc::new( - typed_args - .clone() - .iter() - .cloned() - .take(v1_rt::int_add(idx.clone(), 1) as usize) - .collect::>(), - ) + Rc::new(v1_rt::list_take( + &typed_args.clone(), + v1_rt::int_add(idx.clone(), 1), + )) .iter() .cloned() .fold( @@ -9184,10 +9103,8 @@ pub fn call_arg_bound_param_at( std::option::Option::None => Rc::new(CallArgBoundParamAccum { bound: positionally_eligible_names .clone() - .iter() - .cloned() - .skip(acc.pos.clone() as usize) - .next(), + .get((acc.pos.clone()) as usize) + .cloned(), pos: v1_rt::int_add(acc.pos.clone(), 1), }), }, @@ -9406,10 +9323,8 @@ pub fn direct_call_shape_diags( if ((positional_args.clone().len() as i64) > positional_capacity.clone()) { match positional_args .clone() - .iter() + .get((positional_capacity.clone()) as usize) .cloned() - .skip(positional_capacity.clone() as usize) - .next() { Some(overflow) => Rc::new(vec![crate::v1_std_core::make_error_node( Rc::new(CompilerDiagnostic::CallPositionalSurplus { @@ -9613,10 +9528,8 @@ pub fn function_value_call_named_arg_diags( if (supplied.clone() > param_count.clone()) { match typed_args .clone() - .iter() + .get((param_count.clone()) as usize) .cloned() - .skip(param_count.clone() as usize) - .next() { Some(overflow) => Rc::new(vec![crate::v1_std_core::make_error_node( Rc::new(CompilerDiagnostic::CallPositionalSurplus { @@ -9715,7 +9628,7 @@ pub fn direct_call_arg_mismatch_diags( __result.extend((*{ let formal = app.formal.clone().declaration_bound_conformance.clone(); match app.matched_argument_index.clone() { - Some(arg_index) => match typed_args.clone().iter().cloned().skip(arg_index.clone() as usize).next() { + Some(arg_index) => match typed_args.clone().get((arg_index.clone()) as usize).cloned() { Some(ta) => { let actual_expr = crate::v1_std_core::arg_value(ta.clone()); if match (*actual_expr.expr_data.clone()).clone() { @@ -9802,7 +9715,7 @@ pub fn builtin_text_argument_crossing_diags( let arg = pair.1.clone(); let bound_name = match crate::v1_std_core::arg_name_at(arg.clone(), scope.type_env.clone().source_indices.clone()) { Some(label) => label.clone(), - std::option::Option::None => match formal_names.clone().iter().cloned().skip(pair.0.clone() as usize).next() { + std::option::Option::None => match formal_names.clone().get((pair.0.clone()) as usize).cloned() { Some(n) => n.clone(), std::option::Option::None => "".to_string(), }, @@ -10019,7 +9932,7 @@ pub fn infer_tier2b_builtin_with_kernel_diags( let operand_elem = if ((func_name.clone() == "set_insert".to_string()) || (func_name.clone() == "set_contains".to_string())) { - match typed_args.clone().iter().cloned().skip(1 as usize).next() { + match typed_args.clone().get((1) as usize).cloned() { Some(insert_arg) => { match crate::v1_std_core::arg_value(insert_arg.clone()) .inferred @@ -10036,7 +9949,7 @@ pub fn infer_tier2b_builtin_with_kernel_diags( std::option::Option::None => std::option::Option::None, } } else { - match typed_args.clone().iter().cloned().skip(1 as usize).next() { + match typed_args.clone().get((1) as usize).cloned() { Some(other_arg) => match crate::v1_std_core::arg_value(other_arg.clone()).inferred.clone().as_deref().cloned() { Some(InferredNode::Resolved { node: other_type, .. }) => match crate::v1_compiler_infer_lookup::set_element_type_in_env(other_type.clone(), scope.type_env.clone()) { Some(elem_slot) => Some(crate::v1_compiler_infer_types::child_type_node(elem_slot.clone())), @@ -10055,7 +9968,7 @@ pub fn infer_tier2b_builtin_with_kernel_diags( }; let union_other_bad = if (func_name.clone() == "set_union".to_string()) { - match typed_args.clone().iter().cloned().skip(1 as usize).next() { + match typed_args.clone().get((1) as usize).cloned() { Some(other_arg) => set_union_other_operand_is_resolved_non_set( other_arg.clone(), scope.clone(), @@ -11743,7 +11656,7 @@ Rc::new(ArgInferResult { diagnostics: v1_rt::concat(ar.diagnostics.clone(), Rc::new(vec![contract_diag.clone()])), }) }, - DeclaredArgContract::ContractKnown { types: declared_types, .. } => match declared_types.clone().iter().cloned().skip(idx.clone() as usize).next() { + DeclaredArgContract::ContractKnown { types: declared_types, .. } => match declared_types.clone().get((idx.clone()) as usize).cloned() { Some(declared_type) => infer_arg_with_element_type(a.clone(), declared_type.clone(), nf_scope.clone()), std::option::Option::None => { let ar = infer_expr(nf_lam_value.clone(), nf_scope.clone(), std::option::Option::None); @@ -12006,10 +11919,8 @@ pub fn infer_call_arguments_generic_pass( formal_index, .. } => match formals .clone() - .iter() + .get((formal_index.clone()) as usize) .cloned() - .skip(formal_index.clone() as usize) - .next() { Some(carried) => carried.substitution_basis.clone(), std::option::Option::None => error_type(), @@ -12040,10 +11951,8 @@ pub fn infer_call_arguments_generic_pass( formal_index, .. } => match formals .clone() - .iter() + .get((formal_index.clone()) as usize) .cloned() - .skip(formal_index.clone() as usize) - .next() { Some(carried) => substitute_generics( carried.declaration_bound_conformance.clone(), @@ -12061,10 +11970,8 @@ pub fn infer_call_arguments_generic_pass( formal_index, .. } => match formals .clone() - .iter() + .get((formal_index.clone()) as usize) .cloned() - .skip(formal_index.clone() as usize) - .next() { Some(carried) => { formal_code_point_view(carried.clone(), scope.type_env.clone()) @@ -13304,14 +13211,7 @@ Rc::new(InferResult { } __found }; - let call_method_args = Rc::new( - call_args - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let call_method_args = Rc::new(v1_rt::list_skip(&call_args.clone(), 1)); let call_method_name = Some(func_name.clone()); let call_fold_info = extract_fold_init_info( call_method_name.clone(), @@ -13739,14 +13639,8 @@ Rc::new(InferResult { { { let receiver = method_receiver.clone(); - let remaining = Rc::new( - typed_args - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let remaining = + Rc::new(v1_rt::list_skip(&typed_args.clone(), 1)); let rest_result = rest_operation_result_type( method_resolution.clone(), texpr.span.clone(), @@ -14613,23 +14507,16 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), }, }; let unified_arm_type = match arm_body_types.clone().first().cloned() { - Some(first_type) => Rc::new( - arm_body_types - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - .fold(first_type.clone(), |acc: _, t: Rc| { - crate::v1_compiler_infer_types::prefer_specific_type( - acc, - t.clone(), - scope.type_env.clone().source_indices.clone(), - ) - }), + Some(first_type) => Rc::new(v1_rt::list_skip(&arm_body_types.clone(), 1)) + .iter() + .cloned() + .fold(first_type.clone(), |acc: _, t: Rc| { + crate::v1_compiler_infer_types::prefer_specific_type( + acc, + t.clone(), + scope.type_env.clone().source_indices.clone(), + ) + }), std::option::Option::None => scrut_rt.clone(), }; let arm_infer_results = if crate::v1_compiler_infer_types::is_fully_resolved( @@ -15411,15 +15298,7 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), texpr.clone(), scope.type_env.clone().source_indices.clone(), ); - let lam_param_nodes = Rc::new( - texpr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let lam_param_nodes = Rc::new(v1_rt::list_skip(&texpr.children.clone(), 1)); let elem_prov = match v1_rt::map_get(&scope.lambda_param_provenance.clone(), "elem".to_string()) { Some(p) => p.clone(), @@ -15450,14 +15329,7 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), } else { Rc::new(SubValueRelation::SubValueUnknown) }; - match exp - .params - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { + match exp.params.clone().get((pair.0.clone()) as usize).cloned() { Some(cp) => { let cpt = crate::v1_std_core::param_node_type_expr(cp.clone()); @@ -15615,16 +15487,11 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), { __result.push({ let pn = pair.1.clone(); - let param_name = match lam_params - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { - Some(n) => n.clone(), - std::option::Option::None => "".to_string(), - }; + let param_name = + match lam_params.clone().get((pair.0.clone()) as usize).cloned() { + Some(n) => n.clone(), + std::option::Option::None => "".to_string(), + }; let param_type = match v1_rt::map_get(&lam_scope.locals.clone(), param_name.clone()) { Some(binding) => Some(Rc::new(InferredNode::Resolved { @@ -16529,13 +16396,7 @@ pub fn alias_chain_type_arg_subst( pair.1.clone(), env.source_indices.clone(), ); - match type_args - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { + match type_args.clone().get((pair.0.clone()) as usize).cloned() { Some(arg) => v1_rt::rc_map_insert( acc.clone(), slot.clone(), @@ -17034,14 +16895,7 @@ pub fn infer_map_literal( scope: Rc, ) -> Rc { { - let value_expected = match map_node - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + let value_expected = match map_node.children.clone().get((1) as usize).cloned() { Some(v) => Some(v.clone()), std::option::Option::None => std::option::Option::None, }; @@ -18555,10 +18409,8 @@ pub fn maybe_insert_output_field_relation( let composed = match sig .output_provenance .clone() - .iter() + .get((idx.clone()) as usize) .cloned() - .skip(idx.clone() as usize) - .next() { Some(param_map) => { classify_call_via_provenance(val.clone(), param_map.clone(), ctx.clone()) @@ -19549,21 +19401,15 @@ pub fn classify_call_arg_provenance( pub fn merge_argument_relations(rels: Rc>>) -> Rc { match rels.clone().first().cloned() { std::option::Option::None => Rc::new(SubValueRelation::SubValueUnknown), - Some(first_rel) => Rc::new( - rels.clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - .fold( - first_rel.clone(), - |acc: Rc, rel: Rc| { - crate::std_induction::meet_sub_value(acc, rel.clone()) - }, - ), + Some(first_rel) => Rc::new(v1_rt::list_skip(&rels.clone(), 1)) + .iter() + .cloned() + .fold( + first_rel.clone(), + |acc: Rc, rel: Rc| { + crate::std_induction::meet_sub_value(acc, rel.clone()) + }, + ), } } @@ -20112,10 +19958,8 @@ pub fn build_call_evidence( } std::option::Option::None => match positional_args .clone() - .iter() + .get((def_idx.clone()) as usize) .cloned() - .skip(def_idx.clone() as usize) - .next() { Some(arg_val) => { read_arg_provenance(arg_val.clone(), pname.clone(), ctx.clone()) @@ -20273,7 +20117,7 @@ let p1 = match lparams.clone().first().cloned() { Some(n) => n.clone(), std::option::Option::None => "".to_string(), }; -let p2 = match lparams.clone().iter().cloned().skip(1 as usize).next() { +let p2 = match lparams.clone().get((1) as usize).cloned() { Some(n) => n.clone(), std::option::Option::None => "".to_string(), }; @@ -21027,10 +20871,8 @@ Rc::new(BlockAnnotateAcc { ); lparams .clone() - .iter() + .get((elem_position.clone().unwrap()) as usize) .cloned() - .skip(elem_position.clone().unwrap() as usize) - .next() } std::option::Option::None => std::option::Option::None, }; @@ -22118,12 +21960,7 @@ pub fn meet_per_field_results( let mut __result = Vec::new(); for r in results.iter().cloned() { __result.push( - match r - .clone() - .iter() - .cloned() - .skip(idx.clone() as usize) - .next() + match r.clone().get((idx.clone()) as usize).cloned() { Some(m) => m.clone(), std::option::Option::None => empty_prov_map(), @@ -22153,23 +21990,16 @@ pub fn meet_body_provenance_maps( ) -> Rc>> { match provs.clone().first().cloned() { std::option::Option::None => empty_prov_map(), - Some(first_prov) => Rc::new( - provs - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - .fold( - first_prov.clone(), - |acc: Rc>>, - prov: Rc>>| { - intersect_prov_maps(acc, prov.clone()) - }, - ), + Some(first_prov) => Rc::new(v1_rt::list_skip(&provs.clone(), 1)) + .iter() + .cloned() + .fold( + first_prov.clone(), + |acc: Rc>>, + prov: Rc>>| { + intersect_prov_maps(acc, prov.clone()) + }, + ), } } @@ -22655,14 +22485,10 @@ if ((Rc::new(v1_rt::map_keys(&composed_field_map)).len() as i64) > 0) { acc.clone() } else { { - let final_lp = Rc::new( - stmts - .clone() - .iter() - .cloned() - .take(v1_rt::int_sub(stmt_count.clone(), 1) as usize) - .collect::>(), - ) + let final_lp = Rc::new(v1_rt::list_take( + &stmts.clone(), + v1_rt::int_sub(stmt_count.clone(), 1), + )) .iter() .cloned() .fold( @@ -22693,10 +22519,8 @@ if ((Rc::new(v1_rt::map_keys(&composed_field_map)).len() as i64) > 0) { ); match stmts .clone() - .iter() + .get((v1_rt::int_sub(stmt_count.clone(), 1)) as usize) .cloned() - .skip(v1_rt::int_sub(stmt_count.clone(), 1) as usize) - .next() { Some(last) => collect_variant_constructors( last.clone(), @@ -24007,15 +23831,7 @@ pub fn unify_callable_generics( pub fn lambda_callable_type(lambda: Rc) -> Rc { crate::v1_compiler_infer_types::make_callable_type( - Rc::new( - lambda - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&lambda.children.clone(), 1)), crate::v1_compiler_infer_types::resolved_type(lambda.clone()), ) } @@ -24041,10 +23857,8 @@ pub fn unify_callable_param_pairs( match formals.clone().first().cloned() { Some(f) => match actuals.clone().first().cloned() { Some(a) => { - let __tco_0 = - Rc::new(formals.iter().cloned().skip(1 as usize).collect::>()); - let __tco_1 = - Rc::new(actuals.iter().cloned().skip(1 as usize).collect::>()); + let __tco_0 = Rc::new(v1_rt::list_skip(&formals, 1)); + let __tco_1 = Rc::new(v1_rt::list_skip(&actuals, 1)); let __tco_2 = generic_names.clone(); let __tco_3 = source_indices.clone(); let __tco_4 = unify_generics( @@ -24548,30 +24362,23 @@ pub fn union_parent_type_env_caches( cache: crate::v1_compiler_infer_env::empty_type_env_cache(), conflicts: Rc::new(vec![]), }), - Some(head) => Rc::new( - parent_caches - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - .fold( - Rc::new(GuardedTypeEnvCacheMerge { - cache: head.cache.clone(), - conflicts: Rc::new(vec![]), - }), - |acc: Rc, row: Rc| { - crate::v1_compiler_infer_env::merge_type_env_cache_guarded( - acc.cache.clone(), - row.cache.clone(), - row.import_path.clone(), - acc.conflicts.clone(), - ) - }, - ), + Some(head) => Rc::new(v1_rt::list_skip(&parent_caches.clone(), 1)) + .iter() + .cloned() + .fold( + Rc::new(GuardedTypeEnvCacheMerge { + cache: head.cache.clone(), + conflicts: Rc::new(vec![]), + }), + |acc: Rc, row: Rc| { + crate::v1_compiler_infer_env::merge_type_env_cache_guarded( + acc.cache.clone(), + row.cache.clone(), + row.import_path.clone(), + acc.conflicts.clone(), + ) + }, + ), } } } @@ -27547,25 +27354,18 @@ pub fn build_type_env( source_indices.clone(), ); let parent_inductive_fields = match scope_parents.clone().first().cloned() { - Some(head_parent) => Rc::new( - scope_parents - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - .fold( - head_parent.inductive_fields.clone(), - |acc: Rc>>>>, parent: Rc| { - crate::v1_compiler_infer_env::merge_inductive_fields( - acc, - parent.inductive_fields.clone(), - ) - }, - ), + Some(head_parent) => Rc::new(v1_rt::list_skip(&scope_parents.clone(), 1)) + .iter() + .cloned() + .fold( + head_parent.inductive_fields.clone(), + |acc: Rc>>>>, parent: Rc| { + crate::v1_compiler_infer_env::merge_inductive_fields( + acc, + parent.inductive_fields.clone(), + ) + }, + ), std::option::Option::None => { v1_rt::rc_empty_map::>>>() } @@ -28161,25 +27961,18 @@ pub fn build_type_env_unresolved( source_indices.clone(), ); let parent_inductive_fields = match scope_parents.clone().first().cloned() { - Some(head_parent) => Rc::new( - scope_parents - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() - .fold( - head_parent.inductive_fields.clone(), - |acc: Rc>>>>, parent: Rc| { - crate::v1_compiler_infer_env::merge_inductive_fields( - acc, - parent.inductive_fields.clone(), - ) - }, - ), + Some(head_parent) => Rc::new(v1_rt::list_skip(&scope_parents.clone(), 1)) + .iter() + .cloned() + .fold( + head_parent.inductive_fields.clone(), + |acc: Rc>>>>, parent: Rc| { + crate::v1_compiler_infer_env::merge_inductive_fields( + acc, + parent.inductive_fields.clone(), + ) + }, + ), std::option::Option::None => { v1_rt::rc_empty_map::>>>() } @@ -28511,13 +28304,7 @@ pub fn fold_module_contributions( std::option::Option::None => svc_locals.clone(), }; { - let __tco_0 = Rc::new( - remaining - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_0 = Rc::new(v1_rt::list_skip(&remaining, 1)); let __tco_1 = v1_rt::rc_list_push(resolved_items, contribution.resolved_item.clone()); let __tco_2 = next_func_sigs.clone(); diff --git a/src/v1/stage0/src/v1_compiler_infer_access.rs b/src/v1/stage0/src/v1_compiler_infer_access.rs index 135f7d12523..c22068e6c1a 100644 --- a/src/v1/stage0/src/v1_compiler_infer_access.rs +++ b/src/v1/stage0/src/v1_compiler_infer_access.rs @@ -86,7 +86,7 @@ pub fn keyed_collection_parts( && ((n.children.clone().len() as i64) >= 2)) { match n.children.clone().first().cloned() { - Some(key_child) => match n.children.clone().iter().cloned().skip(1 as usize).next() { + Some(key_child) => match n.children.clone().get((1) as usize).cloned() { Some(value_child) => Some(Rc::new(KeyedCollectionParts { key_type: crate::v1_compiler_infer_types::resolved_type(key_child.clone()), value_type: crate::v1_compiler_infer_types::resolved_type(value_child.clone()), diff --git a/src/v1/stage0/src/v1_compiler_infer_env.rs b/src/v1/stage0/src/v1_compiler_infer_env.rs index 3732f70645d..f6b0a8d5201 100644 --- a/src/v1/stage0/src/v1_compiler_infer_env.rs +++ b/src/v1/stage0/src/v1_compiler_infer_env.rs @@ -1144,14 +1144,7 @@ pub fn segment_lcp_len(a: Rc>, b: Rc>) -> i64 { Some(bh) => { if (bh.clone() == seg.clone()) { Rc::new(SegmentLcpScan { - remaining: Rc::new( - acc.remaining - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + remaining: Rc::new(v1_rt::list_skip(&acc.remaining.clone(), 1)), matched: v1_rt::int_add(acc.matched.clone(), 1), live: true, }) @@ -1635,13 +1628,10 @@ pub fn qualified_all_but_last(name: String) -> String { "".to_string() } else { { - let scan = Rc::new( - segs.clone() - .iter() - .cloned() - .take(v1_rt::int_sub(seg_count.clone(), 1) as usize) - .collect::>(), - ) + let scan = Rc::new(v1_rt::list_take( + &segs.clone(), + v1_rt::int_sub(seg_count.clone(), 1), + )) .iter() .cloned() .fold("".to_string(), |acc: String, seg: String| { diff --git a/src/v1/stage0/src/v1_compiler_infer_lookup.rs b/src/v1/stage0/src/v1_compiler_infer_lookup.rs index 2dc2196ddc6..f35b3584317 100644 --- a/src/v1/stage0/src/v1_compiler_infer_lookup.rs +++ b/src/v1/stage0/src/v1_compiler_infer_lookup.rs @@ -1003,10 +1003,8 @@ pub fn lookup_field_type_node( let value_child = match n .children .clone() - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(child) => { crate::v1_compiler_infer_types::child_type_node( @@ -1272,14 +1270,7 @@ pub fn map_value_type_in_env(type_node: Rc, env: Rc) -> Option= 2)) { - match map_type - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + match map_type.children.clone().get((1) as usize).cloned() { Some(value_type) => Some(value_type.clone()), std::option::Option::None => std::option::Option::None, } @@ -1641,14 +1632,7 @@ pub fn declared_arg_types_for_method( std::option::Option::None => false, }; let non_receiver_templates = if first_is_self.clone() { - Rc::new( - t.param_types - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) + Rc::new(v1_rt::list_skip(&t.param_types.clone(), 1)) } else { t.param_types.clone() }; diff --git a/src/v1/stage0/src/v1_compiler_infer_patterns.rs b/src/v1/stage0/src/v1_compiler_infer_patterns.rs index e2ab2807ad2..5e999c27c36 100644 --- a/src/v1/stage0/src/v1_compiler_infer_patterns.rs +++ b/src/v1/stage0/src/v1_compiler_infer_patterns.rs @@ -75,10 +75,8 @@ pub fn generic_use_slot_bindings( match scrut_node .children .clone() - .iter() + .get((pair.0.clone()) as usize) .cloned() - .skip(pair.0.clone() as usize) - .next() { Some(arg) => v1_rt::rc_map_insert(acc.clone(), slot.clone(), arg.clone()), std::option::Option::None => acc.clone(), @@ -1065,13 +1063,7 @@ pub fn specialize_pattern_row( ) -> Rc>>>> { { let head = pattern_row_head(row.clone()); - let rest = Rc::new( - row.clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest = Rc::new(v1_rt::list_skip(&row.clone(), 1)); if crate::v1_std_core::match_pattern_is_irrefutable(head.clone()) { Rc::new(vec![v1_rt::concat( Rc::new({ @@ -1142,10 +1134,8 @@ pub fn render_constructor_witness( Rc::new(vec![]), |acc: _, pair: (i64, Rc)| match cells .clone() - .iter() + .get((pair.0.clone()) as usize) .cloned() - .skip(pair.0.clone() as usize) - .next() { Some(cell) => { if (cell.clone() == "_".to_string()) { @@ -1210,13 +1200,7 @@ pub fn default_pattern_rows( .iter() .cloned() { - __result.push(Rc::new( - r.clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - )); + __result.push(Rc::new(v1_rt::list_skip(&r.clone(), 1))); } __result }) @@ -1240,14 +1224,7 @@ pub fn exhaustiveness_witnesses( } } Some(head_type) => { - let rest_columns = Rc::new( - columns - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest_columns = Rc::new(v1_rt::list_skip(&columns.clone(), 1)); match (*constructor_roster_for(head_type.clone(), env.clone())).clone() { ConstructorRoster::ConstructorsOpen => { let defaulted = default_pattern_rows(rows.clone()); @@ -1376,24 +1353,15 @@ pub fn exhaustiveness_witnesses( Rc::new(vec![render_constructor_witness( c.clone(), fields.clone(), - Rc::new( - w.cells - .clone() - .iter() - .cloned() - .take(field_count.clone() - as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_take( + &w.cells.clone(), + field_count.clone(), + )), )]), - Rc::new( - w.cells - .clone() - .iter() - .cloned() - .skip(field_count.clone() as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip( + &w.cells.clone(), + field_count.clone(), + )), ), })); } diff --git a/src/v1/stage0/src/v1_compiler_infer_resolve.rs b/src/v1/stage0/src/v1_compiler_infer_resolve.rs index 868d3fc403a..af5f2911654 100644 --- a/src/v1/stage0/src/v1_compiler_infer_resolve.rs +++ b/src/v1/stage0/src/v1_compiler_infer_resolve.rs @@ -101,7 +101,7 @@ pub fn type_param_kind_diagnostics( { __result.extend((*match crate::v1_std_core::find_property(pair.1.clone().properties.clone(), crate::v1_std_core::type_param_kind_property_name(), env.source_indices.clone()) { std::option::Option::None => Rc::new(vec![]), - Some(kind_node) => match carrier.children.clone().iter().cloned().skip(pair.0.clone() as usize).next() { + Some(kind_node) => match carrier.children.clone().get((pair.0.clone()) as usize).cloned() { std::option::Option::None => Rc::new(vec![]), Some(arg) => { let kind_name = crate::v1_compiler_infer_env::authored_name(env.clone(), kind_node.clone()); @@ -1775,17 +1775,11 @@ Rc::new(NodeResolveResult { Some(k) => k.clone(), std::option::Option::None => unit_type(), }; - let val_child_node = match n - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { - Some(v) => v.clone(), - std::option::Option::None => unit_type(), - }; + let val_child_node = + match n.children.clone().get((1) as usize).cloned() { + Some(v) => v.clone(), + std::option::Option::None => unit_type(), + }; let key_type = crate::v1_compiler_infer_types::child_type_node( key_child_node.clone(), ); @@ -3034,25 +3028,20 @@ pub fn resolve_expr_types( }) } }; - let body_r = match arm_ch - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { - Some(b) => resolve_expr_types( - b.clone(), - env.clone(), - module_name.clone(), - ), - std::option::Option::None => { - Rc::new(ExprResolveResult { - expr: child.clone(), - diagnostics: Rc::new(vec![]), - }) - } - }; + let body_r = + match arm_ch.clone().get((1) as usize).cloned() { + Some(b) => resolve_expr_types( + b.clone(), + env.clone(), + module_name.clone(), + ), + std::option::Option::None => { + Rc::new(ExprResolveResult { + expr: child.clone(), + diagnostics: Rc::new(vec![]), + }) + } + }; Rc::new(ExprResolveResult { expr: crate::v1_std_core::make_arm_node( child.occurrence_identity.clone(), @@ -3144,14 +3133,14 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let tr = match ch.clone().iter().cloned().skip(1 as usize).next() { + let tr = match ch.clone().get((1) as usize).cloned() { Some(t) => resolve_expr_types(t.clone(), env.clone(), module_name.clone()), std::option::Option::None => Rc::new(ExprResolveResult { expr: texpr.clone(), diagnostics: Rc::new(vec![]), }), }; - let er = match ch.clone().iter().cloned().skip(2 as usize).next() { + let er = match ch.clone().get((2) as usize).cloned() { Some(e) => Some(resolve_expr_types( e.clone(), env.clone(), @@ -3189,7 +3178,7 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let br = match ch.clone().iter().cloned().skip(1 as usize).next() { + let br = match ch.clone().get((1) as usize).cloned() { Some(bd) => Some(resolve_expr_types( bd.clone(), env.clone(), @@ -3395,7 +3384,7 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let rr = match ch.clone().iter().cloned().skip(1 as usize).next() { + let rr = match ch.clone().get((1) as usize).cloned() { Some(r) => resolve_expr_types(r.clone(), env.clone(), module_name.clone()), std::option::Option::None => Rc::new(ExprResolveResult { expr: texpr.clone(), @@ -3437,15 +3426,7 @@ pub fn resolve_expr_types( }) } ExprData::ExprLambda => { - let lam_param_nodes = Rc::new( - texpr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let lam_param_nodes = Rc::new(v1_rt::list_skip(&texpr.children.clone(), 1)); let r = match texpr.children.clone().first().cloned() { Some(b) => resolve_expr_types(b.clone(), env.clone(), module_name.clone()), std::option::Option::None => Rc::new(ExprResolveResult { @@ -3571,7 +3552,7 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let tr = match ch.clone().iter().cloned().skip(1 as usize).next() { + let tr = match ch.clone().get((1) as usize).cloned() { Some(target) => resolve_node(target.clone(), env.clone(), module_name.clone()), std::option::Option::None => Rc::new(NodeResolveResult { resolved: unit_type(), @@ -3598,7 +3579,7 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let br = match ch.clone().iter().cloned().skip(1 as usize).next() { + let br = match ch.clone().get((1) as usize).cloned() { Some(b) => resolve_expr_types(b.clone(), env.clone(), module_name.clone()), std::option::Option::None => Rc::new(ExprResolveResult { expr: texpr.clone(), @@ -3632,7 +3613,7 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let ir = match ch.clone().iter().cloned().skip(1 as usize).next() { + let ir = match ch.clone().get((1) as usize).cloned() { Some(index) => { resolve_expr_types(index.clone(), env.clone(), module_name.clone()) } @@ -3663,7 +3644,7 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let sr = match ch.clone().iter().cloned().skip(1 as usize).next() { + let sr = match ch.clone().get((1) as usize).cloned() { Some(start) => { resolve_expr_types(start.clone(), env.clone(), module_name.clone()) } @@ -3672,7 +3653,7 @@ pub fn resolve_expr_types( diagnostics: Rc::new(vec![]), }), }; - let er = match ch.clone().iter().cloned().skip(2 as usize).next() { + let er = match ch.clone().get((2) as usize).cloned() { Some(end_e) => { resolve_expr_types(end_e.clone(), env.clone(), module_name.clone()) } @@ -3775,14 +3756,10 @@ pub fn first_duplicate_type_param_name(names: Rc>) -> Option { if { let mut __found = false; - for other in Rc::new( - names - .clone() - .iter() - .cloned() - .skip(v1_rt::int_add(pair.0.clone(), 1) as usize) - .collect::>(), - ) + for other in Rc::new(v1_rt::list_skip( + &names.clone(), + v1_rt::int_add(pair.0.clone(), 1), + )) .iter() .cloned() { @@ -3968,15 +3945,7 @@ pub fn reference_param_with_declaration( binders.clone(), env.clone(), )]), - Rc::new( - param - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(¶m.children.clone(), 1)), ), param.params.clone(), param.inferred.clone(), @@ -4150,15 +4119,7 @@ pub fn binder_marked_param( binders.clone(), env.clone(), )]), - Rc::new( - param - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(¶m.children.clone(), 1)), ), param.params.clone(), param.inferred.clone(), diff --git a/src/v1/stage0/src/v1_compiler_infer_types.rs b/src/v1/stage0/src/v1_compiler_infer_types.rs index 04ee1a3e91b..f9dc4f910d1 100644 --- a/src/v1/stage0/src/v1_compiler_infer_types.rs +++ b/src/v1/stage0/src/v1_compiler_infer_types.rs @@ -1487,14 +1487,7 @@ pub fn unify_template( } std::option::Option::None => subst.clone(), }; - match concrete - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + match concrete.children.clone().get((1) as usize).cloned() { Some(v) => { if (v1_rt::map_get(&s1, "__value__".to_string()) != std::option::Option::None) @@ -1613,14 +1606,7 @@ pub fn unify_template( ), std::option::Option::None => subst.clone(), }; - match concrete - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + match concrete.children.clone().get((1) as usize).cloned() { Some(c) => unify_template( st.clone(), child_type_node(c.clone()), @@ -1656,14 +1642,7 @@ pub fn build_type_substitution( std::option::Option::None => false, }; let non_receiver_templates = if first_is_self.clone() { - Rc::new( - param_templates - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) + Rc::new(v1_rt::list_skip(¶m_templates.clone(), 1)) } else { param_templates.clone() }; @@ -1897,46 +1876,41 @@ pub fn apply_type_substitution( } }, }, - AlgebraTypeTemplate::ReceiverValue => match receiver - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { - Some(child) => Rc::new(KernelTypeBuild { - ty: child_type_node(child.clone()), - diagnostics: Rc::new(vec![]), - }), - std::option::Option::None => { - match v1_rt::map_get(&subst, "__value__".to_string()) { - Some(val) => Rc::new(KernelTypeBuild { - ty: val.clone(), - diagnostics: Rc::new(vec![]), - }), - std::option::Option::None => { - let rname = - container_kind_canonical(crate::v1_std_core::authored_name_at( - source_indices.clone(), - receiver.clone(), - )); - match crate::std_types::container_param_name(rname.clone(), 1) { - Some(n) => Rc::new(KernelTypeBuild { - ty: type_variable_node(n.clone()), - diagnostics: Rc::new(vec![]), - }), - std::option::Option::None => Rc::new(KernelTypeBuild { - ty: missing_kernel_container_profile_type(rname.clone()), - diagnostics: Rc::new(vec![ - kernel_container_profile_miss_diagnostic(rname.clone()), - ]), - }), + AlgebraTypeTemplate::ReceiverValue => { + match receiver.children.clone().get((1) as usize).cloned() { + Some(child) => Rc::new(KernelTypeBuild { + ty: child_type_node(child.clone()), + diagnostics: Rc::new(vec![]), + }), + std::option::Option::None => { + match v1_rt::map_get(&subst, "__value__".to_string()) { + Some(val) => Rc::new(KernelTypeBuild { + ty: val.clone(), + diagnostics: Rc::new(vec![]), + }), + std::option::Option::None => { + let rname = + container_kind_canonical(crate::v1_std_core::authored_name_at( + source_indices.clone(), + receiver.clone(), + )); + match crate::std_types::container_param_name(rname.clone(), 1) { + Some(n) => Rc::new(KernelTypeBuild { + ty: type_variable_node(n.clone()), + diagnostics: Rc::new(vec![]), + }), + std::option::Option::None => Rc::new(KernelTypeBuild { + ty: missing_kernel_container_profile_type(rname.clone()), + diagnostics: Rc::new(vec![ + kernel_container_profile_miss_diagnostic(rname.clone()), + ]), + }), + } } } } } - }, + } AlgebraTypeTemplate::NamedTemplate { name: n, .. } => Rc::new(KernelTypeBuild { ty: nominal_type_ref(n.clone()), diagnostics: Rc::new(vec![]), @@ -2221,13 +2195,7 @@ pub fn node_type_shape_argument_list( match args.clone().first().cloned() { Some(a) => { let head = node_type_shape(child_type_node(a.clone()), source_indices.clone()); - let rest = Rc::new( - args.clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let rest = Rc::new(v1_rt::list_skip(&args.clone(), 1)); if ((rest.clone().len() as i64) == 0) { head.clone() } else { @@ -2830,14 +2798,7 @@ pub fn callable_signatures_agree( .iter() .cloned() { - if !(match right - .params - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() - { + if !(match right.params.clone().get((pair.0.clone()) as usize).cloned() { Some(right_param) => agree( crate::v1_std_core::param_node_type_expr(pair.1.clone()), crate::v1_std_core::param_node_type_expr(right_param.clone()), @@ -2940,10 +2901,8 @@ pub fn node_type_equals_core( if !(match right .children .clone() - .iter() + .get((pair.0.clone()) as usize) .cloned() - .skip(pair.0.clone() as usize) - .next() { Some(right_child) => node_type_equals( pair.1.clone(), @@ -3026,18 +2985,14 @@ pub fn node_type_equals_core( Some(right_first) => match left .children .clone() - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(left_second) => match right .children .clone() - .iter() + .get((1) as usize) .cloned() - .skip(1 as usize) - .next() { Some(right_second) => { (node_type_equals( @@ -3241,14 +3196,7 @@ pub fn method_receiver_element_node( { let normed = normalize_access_type_node(receiver_type.clone()); let maybe_element = if node_is_keyed_collection(normed.clone(), source_indices.clone()) { - match normed - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() - { + match normed.children.clone().get((1) as usize).cloned() { Some(ch) => Some(child_type_node(ch.clone())), std::option::Option::None => std::option::Option::None, } @@ -3362,13 +3310,7 @@ pub fn first_matching_algebra_field( } std::option::Option::None => { let __tco_0 = n; - let __tco_1 = Rc::new( - candidates - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ); + let __tco_1 = Rc::new(v1_rt::list_skip(&candidates, 1)); let __tco_2 = source_indices; __tco_loop_n = __tco_0; __tco_loop_candidates = __tco_1; diff --git a/src/v1/stage0/src/v1_compiler_ownership.rs b/src/v1/stage0/src/v1_compiler_ownership.rs index 12959c145bd..a69c4f25071 100644 --- a/src/v1/stage0/src/v1_compiler_ownership.rs +++ b/src/v1/stage0/src/v1_compiler_ownership.rs @@ -296,14 +296,10 @@ pub fn merge_branch_usages( let mut __result = Vec::new(); for b in branches.iter().cloned() { __result.extend( - (*Rc::new( - b.fold_call_nodes - .clone() - .iter() - .cloned() - .skip(base_fold_count.clone() as usize) - .collect::>(), - )) + (*Rc::new(v1_rt::list_skip( + &b.fold_call_nodes.clone(), + base_fold_count.clone(), + ))) .iter() .cloned(), ); @@ -1294,7 +1290,7 @@ pub fn analyze_single_fold( Some(a) => crate::v1_std_core::arg_value(a.clone()), std::option::Option::None => method_call.clone(), }; - let fold_lambda_node = match args.clone().iter().cloned().skip(1 as usize).next() { + let fold_lambda_node = match args.clone().get((1) as usize).cloned() { Some(a) => crate::v1_std_core::arg_value(a.clone()), std::option::Option::None => method_call.clone(), }; diff --git a/src/v1/stage0/src/v1_compiler_parse.rs b/src/v1/stage0/src/v1_compiler_parse.rs index 6203bf1f162..1e73153d17c 100644 --- a/src/v1/stage0/src/v1_compiler_parse.rs +++ b/src/v1/stage0/src/v1_compiler_parse.rs @@ -723,8 +723,7 @@ pub fn mint_parsed_string_part_nodes( } }; { - let __tco_0 = - Rc::new(parts.iter().cloned().skip(1 as usize).collect::>()); + let __tco_0 = Rc::new(v1_rt::list_skip(&parts, 1)); let __tco_1 = minted.ctx.clone(); let __tco_2 = span; let __tco_3 = v1_rt::rc_list_push(acc, node.clone()); @@ -3240,14 +3239,7 @@ pub fn stamp_parsed_node_list_with_head_role( } else { { let stamped_tail = stamp_parsed_node_list( - Rc::new( - nodes - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&nodes.clone(), 1)), ancestors.clone(), stamped_head.ctx.clone(), tail_role.clone(), diff --git a/src/v1/stage0/src/v1_compiler_runtime_rust.rs b/src/v1/stage0/src/v1_compiler_runtime_rust.rs index 38d49cab731..c132ac2f070 100644 --- a/src/v1/stage0/src/v1_compiler_runtime_rust.rs +++ b/src/v1/stage0/src/v1_compiler_runtime_rust.rs @@ -76,7 +76,7 @@ pub fn rt_string_ops() -> String { } pub fn rt_collection_ops() -> String { - v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("pub fn lookup(m: &HashMap, key: K) -> Option {\n".to_string(), " m.get(&key).cloned()\n".to_string()), "}\n\n".to_string()), "pub fn list_get_optional(items: &Vec, index: i64) -> Option {\n".to_string()), " if index < 0 { None } else { items.get(index as usize).cloned() }\n".to_string()), "}\n\n".to_string()), "pub fn index_by String>(list: Vec, key_fn: F) -> HashMap {\n".to_string()), " let mut map = HashMap::new();\n".to_string()), " for item in list { let key = key_fn(&item); map.insert(key, item); }\n".to_string()), " map\n".to_string()), "}\n\n".to_string()), "pub fn empty_map() -> HashMap { HashMap::new() }\n\n".to_string()), "pub fn map_insert(mut map: HashMap, key: K, value: V) -> HashMap {\n".to_string()), " map.insert(key, value); map\n".to_string()), "}\n\n".to_string()), "pub fn map_merge(mut base: HashMap, overlay: HashMap) -> HashMap {\n".to_string()), " base.extend(overlay); base\n".to_string()), "}\n\n".to_string()), "pub fn map_get(m: &HashMap, key: K) -> Option {\n".to_string()), " m.get(&key).cloned()\n".to_string()), "}\n\n".to_string()), "pub fn map_keys(m: &HashMap) -> Vec { m.keys().cloned().collect() }\n\n".to_string()), "// THE EMITTED ORDERING-KEY ADMISSION: the counterpart of the interpreter's admit_emitted_ord_keys.\n".to_string()), "// Only String, i64 and bool (and their aliases) are admitted, because on exactly those kinds the\n".to_string()), "// native Ord IS the canonical content order (std.algebra TotalOrder). Any other key type -- a\n".to_string()), "// derived-Ord record or enum (declaration order), or f64 (no total Ord) -- fails to compile here,\n".to_string()), "// at the call, rather than sorting in an order the interpreter would not produce.\n".to_string()), "#[diagnostic::on_unimplemented(message = \"EMIT REFUSED: `{Self}` is not an admitted ordering key; sorted_map_keys and sort_by admit only String, Int and Bool keys, whose order is the canonical content order in both realizations\", label = \"ordering key of a type with no canonical emitted order\")]\n".to_string()), "pub trait CanonicalOrdKey: Ord {}\n".to_string()), "impl CanonicalOrdKey for String {}\n".to_string()), "impl CanonicalOrdKey for RcStr {}\n".to_string()), "impl CanonicalOrdKey for i64 {}\n".to_string()), "impl CanonicalOrdKey for bool {}\n\n".to_string()), "pub fn canonical_key_cmp(a: &K, b: &K) -> std::cmp::Ordering { a.cmp(b) }\n\n".to_string()), "pub fn sorted_map_keys(m: &HashMap) -> Vec { let mut keys = map_keys(m); keys.sort(); keys }\n\n".to_string()), "pub fn map_is_empty(m: &HashMap) -> bool { m.is_empty() }\n\n".to_string()), "// Sound identity hint (see 04_infer.dag substitute_generics): returns true =>\n".to_string()), "// the two are structurally equal. The compiled realization answers with pointer\n".to_string()), "// identity (conservative: may say false when equal); never true-when-unequal, so\n".to_string()), "// callers that return the original on true preserve output structure. Owned Rc\n".to_string()), "// args (cheap refcount bump) keep the inner element pointers, so ptr_eq on the\n".to_string()), "// clones equals ptr_eq on the originals -- lets the .dag pass args by value and\n".to_string()), "// emit through the normal clone path (no all-args-by-ref emit rule needed).\n".to_string()), "pub fn rc_ptr_eq(a: Rc, b: Rc) -> bool { Rc::ptr_eq(&a, &b) }\n\n".to_string()), "pub fn rc_vec_ptr_eq(a: Rc>>, b: Rc>>) -> bool {\n".to_string()), " a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| Rc::ptr_eq(x, y))\n".to_string()), "}\n\n".to_string()), "pub fn map_values(m: &HashMap) -> Vec { m.values().cloned().collect() }\n\n".to_string()), "pub fn list_concat(mut a: Vec, b: Vec) -> Vec { a.append(b); a }\n\n".to_string()), "pub fn list_push(mut list: Vec, item: T) -> Vec { list.push_back(item); list }\n\n".to_string()), "pub fn append(list: Rc>, item: T) -> Vec {\n".to_string()), " let mut v = (*list).clone();\n".to_string()), " v.push_back(item);\n".to_string()), " v\n".to_string()), "}\n\n".to_string()), "pub fn chars_to_string(chars: &Rc>, start: i64, end: i64) -> String {\n".to_string()), " let len = chars.len();\n".to_string()), " let start = (start.max(0) as usize).min(len);\n".to_string()), " let end = (end.max(0) as usize).min(len).max(start);\n".to_string()), " let units = (end.saturating_sub(start)) as u64;\n".to_string()), " record_source_chars_slice_walked(units);\n".to_string()), " // Focus caches the current RRB chunk, so per-char access is contiguous-slice\n".to_string()), " // speed; a plain iter().skip(start) walks O(start) per call, which summed\n".to_string()), " // quadratically across a tokenize pass (profiled 2026-07-06).\n".to_string()), " let mut focus = chars.focus();\n".to_string()), " let mut out = String::with_capacity(end - start);\n".to_string()), " for i in start..end {\n".to_string()), " if let Some(ch) = char::from_u32(*focus.index(i) as u32) {\n".to_string()), " out.push(ch);\n".to_string()), " }\n".to_string()), " }\n".to_string()), " out\n".to_string()), "}\n\n".to_string()), "pub fn parse_int(s: String) -> Option { s.parse::().ok() }\n\n".to_string()), "pub fn map_contains_key(m: &HashMap, key: K) -> bool {\n".to_string()), " m.contains_key(&key)\n".to_string()), "}\n\n".to_string()), "pub fn map_has(m: &HashMap, key: K) -> bool {\n".to_string()), " m.contains_key(&key)\n".to_string()), "}\n\n".to_string()), "pub fn rc_empty_set() -> Rc> {\n".to_string()), " Rc::new(BTreeSet::new())\n".to_string()), "}\n\n".to_string()), "pub fn rc_set_insert(mut s: Rc>, x: T) -> Rc> {\n".to_string()), " Rc::make_mut(&mut s).insert(x);\n".to_string()), " s\n".to_string()), "}\n\n".to_string()), "pub fn rc_set_union(a: Rc>, b: Rc>) -> Rc> {\n".to_string()), " let mut out = a;\n".to_string()), " for x in b.iter().cloned() {\n".to_string()), " Rc::make_mut(&mut out).insert(x);\n".to_string()), " }\n".to_string()), " out\n".to_string()), "}\n\n".to_string()), "pub fn set_contains>>(s: S, x: T) -> bool {\n".to_string()), " s.as_ref().contains(&x)\n".to_string()), "}\n\n".to_string()), "pub fn reverse(list: Rc>) -> Rc> {\n".to_string()), " Rc::new(list.iter().cloned().rev().collect())\n".to_string()), "}\n\n".to_string()), "pub fn replace(s: String, from: String, to: String) -> String {\n".to_string()), " s.replace(&from, &to)\n".to_string()), "}\n\n".to_string()) + v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("pub fn lookup(m: &HashMap, key: K) -> Option {\n".to_string(), " m.get(&key).cloned()\n".to_string()), "}\n\n".to_string()), "pub fn list_get_optional(items: &Vec, index: i64) -> Option {\n".to_string()), " if index < 0 { None } else { items.get(index as usize).cloned() }\n".to_string()), "}\n\n".to_string()), "pub fn index_by String>(list: Vec, key_fn: F) -> HashMap {\n".to_string()), " let mut map = HashMap::new();\n".to_string()), " for item in list { let key = key_fn(&item); map.insert(key, item); }\n".to_string()), " map\n".to_string()), "}\n\n".to_string()), "pub fn empty_map() -> HashMap { HashMap::new() }\n\n".to_string()), "pub fn map_insert(mut map: HashMap, key: K, value: V) -> HashMap {\n".to_string()), " map.insert(key, value); map\n".to_string()), "}\n\n".to_string()), "pub fn map_merge(mut base: HashMap, overlay: HashMap) -> HashMap {\n".to_string()), " base.extend(overlay); base\n".to_string()), "}\n\n".to_string()), "pub fn map_get(m: &HashMap, key: K) -> Option {\n".to_string()), " m.get(&key).cloned()\n".to_string()), "}\n\n".to_string()), "pub fn map_keys(m: &HashMap) -> Vec { m.keys().cloned().collect() }\n\n".to_string()), "// THE EMITTED ORDERING-KEY ADMISSION: the counterpart of the interpreter's admit_emitted_ord_keys.\n".to_string()), "// Only String, i64 and bool (and their aliases) are admitted, because on exactly those kinds the\n".to_string()), "// native Ord IS the canonical content order (std.algebra TotalOrder). Any other key type -- a\n".to_string()), "// derived-Ord record or enum (declaration order), or f64 (no total Ord) -- fails to compile here,\n".to_string()), "// at the call, rather than sorting in an order the interpreter would not produce.\n".to_string()), "#[diagnostic::on_unimplemented(message = \"EMIT REFUSED: `{Self}` is not an admitted ordering key; sorted_map_keys and sort_by admit only String, Int and Bool keys, whose order is the canonical content order in both realizations\", label = \"ordering key of a type with no canonical emitted order\")]\n".to_string()), "pub trait CanonicalOrdKey: Ord {}\n".to_string()), "impl CanonicalOrdKey for String {}\n".to_string()), "impl CanonicalOrdKey for RcStr {}\n".to_string()), "impl CanonicalOrdKey for i64 {}\n".to_string()), "impl CanonicalOrdKey for bool {}\n\n".to_string()), "pub fn canonical_key_cmp(a: &K, b: &K) -> std::cmp::Ordering { a.cmp(b) }\n\n".to_string()), "pub fn sorted_map_keys(m: &HashMap) -> Vec { let mut keys = map_keys(m); keys.sort(); keys }\n\n".to_string()), "pub fn map_is_empty(m: &HashMap) -> bool { m.is_empty() }\n\n".to_string()), "// Sound identity hint (see 04_infer.dag substitute_generics): returns true =>\n".to_string()), "// the two are structurally equal. The compiled realization answers with pointer\n".to_string()), "// identity (conservative: may say false when equal); never true-when-unequal, so\n".to_string()), "// callers that return the original on true preserve output structure. Owned Rc\n".to_string()), "// args (cheap refcount bump) keep the inner element pointers, so ptr_eq on the\n".to_string()), "// clones equals ptr_eq on the originals -- lets the .dag pass args by value and\n".to_string()), "// emit through the normal clone path (no all-args-by-ref emit rule needed).\n".to_string()), "pub fn rc_ptr_eq(a: Rc, b: Rc) -> bool { Rc::ptr_eq(&a, &b) }\n\n".to_string()), "pub fn rc_vec_ptr_eq(a: Rc>>, b: Rc>>) -> bool {\n".to_string()), " a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| Rc::ptr_eq(x, y))\n".to_string()), "}\n\n".to_string()), "pub fn map_values(m: &HashMap) -> Vec { m.values().cloned().collect() }\n\n".to_string()), "pub fn list_concat(mut a: Vec, b: Vec) -> Vec { a.append(b); a }\n\n".to_string()), "pub fn list_push(mut list: Vec, item: T) -> Vec { list.push_back(item); list }\n\n".to_string()), "/// std.primitives skip_contract on the persistent carrier: the suffix after `n` members,\n".to_string()), "/// sharing the receiver's tree in O(log n) instead of copying the remainder, so a walk that\n".to_string()), "/// slices by offset is O(n log n) rather than quadratic. A negative `n` skips every member, as\n".to_string()), "/// the copying form's saturating `n as usize` did.\n".to_string()), "pub fn list_skip(items: &Vec, n: i64) -> Vec {\n".to_string()), " let k = if n < 0 {\n".to_string()), " items.len()\n".to_string()), " } else {\n".to_string()), " (n as usize).min(items.len())\n".to_string()), " };\n".to_string()), " items.skip(k)\n".to_string()), "}\n".to_string()), "\n".to_string()), "/// std.primitives take_contract on the persistent carrier: the first `n` members, sharing\n".to_string()), "/// the receiver's tree. A negative `n` keeps every member, as the copying form did.\n".to_string()), "pub fn list_take(items: &Vec, n: i64) -> Vec {\n".to_string()), " let k = if n < 0 {\n".to_string()), " items.len()\n".to_string()), " } else {\n".to_string()), " (n as usize).min(items.len())\n".to_string()), " };\n".to_string()), " items.take(k)\n".to_string()), "}\n".to_string()), "\n".to_string()), "#[cfg(test)]\n".to_string()), "mod list_slice_tests {\n".to_string()), " use super::*;\n".to_string()), "\n".to_string()), " thread_local! {\n".to_string()), " static CLONES: Cell = const { Cell::new(0) };\n".to_string()), " }\n".to_string()), "\n".to_string()), " /// A code point whose every copy is counted, so a slice's cost is read off the carrier's\n".to_string()), " /// own work rather than a clock.\n".to_string()), " #[derive(Debug, PartialEq)]\n".to_string()), " struct CountedCodePoint(u32);\n".to_string()), "\n".to_string()), " impl Clone for CountedCodePoint {\n".to_string()), " fn clone(&self) -> Self {\n".to_string()), " CLONES.with(|c| c.set(c.get() + 1));\n".to_string()), " CountedCodePoint(self.0)\n".to_string()), " }\n".to_string()), " }\n".to_string()), "\n".to_string()), " fn clones_during(f: impl FnOnce() -> R) -> (R, u64) {\n".to_string()), " let before = CLONES.with(|c| c.get());\n".to_string()), " let r = f();\n".to_string()), " (r, CLONES.with(|c| c.get()) - before)\n".to_string()), " }\n".to_string()), "\n".to_string()), " // Non-ASCII on purpose: emitted char_at / substring over a bare &str are O(offset) here,\n".to_string()), " // which is the walk the code-point slice replaces.\n".to_string()), " fn non_ascii_code_points(n: usize) -> Vec {\n".to_string()), " [0xe9u32, 0x4e2d, 0x1f600, 0x61]\n".to_string()), " .iter()\n".to_string()), " .cycle()\n".to_string()), " .take(n)\n".to_string()), " .map(|cp| CountedCodePoint(*cp))\n".to_string()), " .collect()\n".to_string()), " }\n".to_string()), "\n".to_string()), " const N: usize = 200_000;\n".to_string()), " // im's RRB split copies at most one boundary chunk per tree level; this is that bound with\n".to_string()), " // headroom, and three orders of magnitude below N.\n".to_string()), " const SLICE_COPY_BUDGET: u64 = 2_048;\n".to_string()), "\n".to_string()), " #[test]\n".to_string()), " fn a_slice_from_the_middle_copies_only_boundary_chunks() {\n".to_string()), " let cps = Rc::new(non_ascii_code_points(N));\n".to_string()), " let (slice, copied) = clones_during(|| list_take(&list_skip(&cps, (N / 2) as i64), 7));\n".to_string()), " assert!(\n".to_string()), " copied <= SLICE_COPY_BUDGET,\n".to_string()), " \"skip+take copied {} members of {}\",\n".to_string()), " copied,\n".to_string()), " N\n".to_string()), " );\n".to_string()), " let expected: std::vec::Vec = cps.iter().skip(N / 2).take(7).map(|c| c.0).collect();\n".to_string()), " assert_eq!(\n".to_string()), " slice.iter().map(|c| c.0).collect::>(),\n".to_string()), " expected\n".to_string()), " );\n".to_string()), " }\n".to_string()), "\n".to_string()), " // THE RED CONTROL: the extdeps.languages.rust.emit skip / take templates this runtime pair\n".to_string()), " // replaced, spelled as they emitted, on the same input and bound. It must exceed the budget,\n".to_string()), " // or the budget does not discriminate the quadratic.\n".to_string()), " #[test]\n".to_string()), " fn the_copying_template_form_exceeds_the_budget() {\n".to_string()), " let cps = Rc::new(non_ascii_code_points(N));\n".to_string()), " let (_, copied) = clones_during(|| {\n".to_string()), " let rest = cps.iter().cloned().skip(N / 2).collect::>();\n".to_string()), " rest.iter().cloned().take(7).collect::>()\n".to_string()), " });\n".to_string()), " assert!(\n".to_string()), " copied > SLICE_COPY_BUDGET,\n".to_string()), " \"copying form copied only {}\",\n".to_string()), " copied\n".to_string()), " );\n".to_string()), " }\n".to_string()), "\n".to_string()), " #[test]\n".to_string()), " fn negative_and_overlong_counts_keep_the_copying_forms_reading() {\n".to_string()), " let xs: Vec = (0..10).collect();\n".to_string()), " for n in [-3i64, 0, 4, 10, 11, i64::MAX] {\n".to_string()), " let copying_skip = xs.iter().cloned().skip(n as usize).collect::>();\n".to_string()), " let copying_take = xs.iter().cloned().take(n as usize).collect::>();\n".to_string()), " assert_eq!(list_skip(&xs, n), copying_skip, \"skip {}\", n);\n".to_string()), " assert_eq!(list_take(&xs, n), copying_take, \"take {}\", n);\n".to_string()), " }\n".to_string()), " }\n".to_string()), "}\n".to_string()), "\n".to_string()), "pub fn append(list: Rc>, item: T) -> Vec {\n".to_string()), " let mut v = (*list).clone();\n".to_string()), " v.push_back(item);\n".to_string()), " v\n".to_string()), "}\n\n".to_string()), "pub fn chars_to_string(chars: &Rc>, start: i64, end: i64) -> String {\n".to_string()), " let len = chars.len();\n".to_string()), " let start = (start.max(0) as usize).min(len);\n".to_string()), " let end = (end.max(0) as usize).min(len).max(start);\n".to_string()), " let units = (end.saturating_sub(start)) as u64;\n".to_string()), " record_source_chars_slice_walked(units);\n".to_string()), " // Focus caches the current RRB chunk, so per-char access is contiguous-slice\n".to_string()), " // speed; a plain iter().skip(start) walks O(start) per call, which summed\n".to_string()), " // quadratically across a tokenize pass (profiled 2026-07-06).\n".to_string()), " let mut focus = chars.focus();\n".to_string()), " let mut out = String::with_capacity(end - start);\n".to_string()), " for i in start..end {\n".to_string()), " if let Some(ch) = char::from_u32(*focus.index(i) as u32) {\n".to_string()), " out.push(ch);\n".to_string()), " }\n".to_string()), " }\n".to_string()), " out\n".to_string()), "}\n\n".to_string()), "pub fn parse_int(s: String) -> Option { s.parse::().ok() }\n\n".to_string()), "pub fn map_contains_key(m: &HashMap, key: K) -> bool {\n".to_string()), " m.contains_key(&key)\n".to_string()), "}\n\n".to_string()), "pub fn map_has(m: &HashMap, key: K) -> bool {\n".to_string()), " m.contains_key(&key)\n".to_string()), "}\n\n".to_string()), "pub fn rc_empty_set() -> Rc> {\n".to_string()), " Rc::new(BTreeSet::new())\n".to_string()), "}\n\n".to_string()), "pub fn rc_set_insert(mut s: Rc>, x: T) -> Rc> {\n".to_string()), " Rc::make_mut(&mut s).insert(x);\n".to_string()), " s\n".to_string()), "}\n\n".to_string()), "pub fn rc_set_union(a: Rc>, b: Rc>) -> Rc> {\n".to_string()), " let mut out = a;\n".to_string()), " for x in b.iter().cloned() {\n".to_string()), " Rc::make_mut(&mut out).insert(x);\n".to_string()), " }\n".to_string()), " out\n".to_string()), "}\n\n".to_string()), "pub fn set_contains>>(s: S, x: T) -> bool {\n".to_string()), " s.as_ref().contains(&x)\n".to_string()), "}\n\n".to_string()), "pub fn reverse(list: Rc>) -> Rc> {\n".to_string()), " Rc::new(list.iter().cloned().rev().collect())\n".to_string()), "}\n\n".to_string()), "pub fn replace(s: String, from: String, to: String) -> String {\n".to_string()), " s.replace(&from, &to)\n".to_string()), "}\n\n".to_string()) } pub fn rt_string_carrier() -> String { diff --git a/src/v1/stage0/src/v1_compiler_trace.rs b/src/v1/stage0/src/v1_compiler_trace.rs index 1637cff79ed..a86f63edfb6 100644 --- a/src/v1/stage0/src/v1_compiler_trace.rs +++ b/src/v1/stage0/src/v1_compiler_trace.rs @@ -98,15 +98,10 @@ pub fn trace_pop_frame(trace: Rc) -> Rc { } else { Rc::new(Trace { events: trace.events.clone(), - stack: Rc::new( - trace - .stack - .clone() - .iter() - .cloned() - .take(v1_rt::int_sub(n.clone(), 1) as usize) - .collect::>(), - ), + stack: Rc::new(v1_rt::list_take( + &trace.stack.clone(), + v1_rt::int_sub(n.clone(), 1), + )), }) } } diff --git a/src/v1/stage0/src/v1_compiler_trait_derive_emit.rs b/src/v1/stage0/src/v1_compiler_trait_derive_emit.rs index 4f1778fa883..e022f34832a 100644 --- a/src/v1/stage0/src/v1_compiler_trait_derive_emit.rs +++ b/src/v1/stage0/src/v1_compiler_trait_derive_emit.rs @@ -2106,22 +2106,8 @@ pub fn v1_ord_propagated_zip_loop( (here.clone() || v1_ord_propagated_zip_loop( param_name.clone(), - Rc::new( - decl_params - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), - Rc::new( - type_args - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&decl_params.clone(), 1)), + Rc::new(v1_rt::list_skip(&type_args.clone(), 1)), decl_set_affected_names.clone(), source_indices.clone(), )) @@ -2775,22 +2761,8 @@ pub fn v1_declared_type_app_mentions_param_non_phantom_loop( (here.clone() || v1_declared_type_app_mentions_param_non_phantom_loop( param_name.clone(), - Rc::new( - decl_params - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), - Rc::new( - type_args - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&decl_params.clone(), 1)), + Rc::new(v1_rt::list_skip(&type_args.clone(), 1)), phantom_slot_names.clone(), bounds.clone(), type_decl_items.clone(), @@ -3652,22 +3624,8 @@ pub fn v1_declared_type_app_clone_impl_needs_param_loop( (here.clone() || v1_declared_type_app_clone_impl_needs_param_loop( param_name.clone(), - Rc::new( - decl_params - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), - Rc::new( - type_args - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&decl_params.clone(), 1)), + Rc::new(v1_rt::list_skip(&type_args.clone(), 1)), phantom_slot_names.clone(), bounds.clone(), type_decl_items.clone(), @@ -3735,22 +3693,8 @@ pub fn v1_declared_arg_positions_need_clone_param( (here.clone() || v1_declared_arg_positions_need_clone_param( param_name.clone(), - Rc::new( - decl_params - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), - Rc::new( - type_args - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&decl_params.clone(), 1)), + Rc::new(v1_rt::list_skip(&type_args.clone(), 1)), bound_params.clone(), bounds.clone(), type_decl_items.clone(), diff --git a/src/v1/stage0/src/v1_interpreter.rs b/src/v1/stage0/src/v1_interpreter.rs index 6ed58544e37..d06324cbc18 100644 --- a/src/v1/stage0/src/v1_interpreter.rs +++ b/src/v1/stage0/src/v1_interpreter.rs @@ -1100,16 +1100,74 @@ fn value_eq_calls() -> u64 { /// onto a heap worklist and dropped from there with its own children already detached. Shared /// children (`Rc` strong count above one) are left in place; their last owner detaches them. /// Class: gunbc.recurring_failure_mode recursion_over_value_depth_uncounted_by_the_call_limit. +/// +/// A PERSISTENT CARRIER IS DETACHED ONLY ONCE DROP NESTING IS DEEP. A List or Map is an RRB / HAMT +/// tree whose nodes may be shared with other values -- a `skip` or `map_insert` result shares +/// all but a logarithmic boundary with its source -- and consuming one into the worklist clones +/// every member out of the shared nodes, so dropping a slice cost its source's length and a walk +/// that slices by offset was quadratic in the drop alone. Detaching exists for host-stack depth, +/// and depth only accrues where one carrier's drop nests another's: below +/// `VALUE_DROP_INLINE_DEPTH` nested drops the carrier is released by its own drop, which frees +/// shared nodes by reference count and runs each member's `Value::drop` (itself iterative over +/// Variant / Record depth); at or beyond it the carrier is detached as before, so a list nested +/// to any depth still drops in bounded host stack (`value_depth_walker_tests::a_deep_list_drops`). impl Drop for Value { fn drop(&mut self) { + let depth = VALUE_DROP_DEPTH + .try_with(|d| { + let depth = d.get(); + d.set(depth + 1); + depth + }) + .unwrap_or(VALUE_DROP_INLINE_DEPTH); + let detach_carriers = depth >= VALUE_DROP_INLINE_DEPTH; let mut pending: Vec = Vec::new(); - detach_owned_children(self, &mut pending); + detach_owned_children(self, &mut pending, detach_carriers); while let Some(mut child) = pending.pop() { - detach_owned_children(&mut child, &mut pending); + detach_owned_children(&mut child, &mut pending, detach_carriers); + } + // A carrier left attached is released HERE, while the depth is raised: drop glue would + // release it only after this function returns and the depth is restored, and a nested + // carrier's members would then never see the depth they are at. + match self { + Value::List(items) => drop(std::mem::replace(items, empty_list_carrier())), + Value::Map(entries) => drop(std::mem::replace(entries, empty_map_carrier())), + _ => {} } + let _ = VALUE_DROP_DEPTH.try_with(|d| d.set(depth)); } } +thread_local! { + static EMPTY_LIST_CARRIER: Rc> = Rc::new(RrbVector::new()); + static EMPTY_MAP_CARRIER: Rc> = Rc::new(HamtMap::new()); +} + +fn empty_list_carrier() -> Rc> { + EMPTY_LIST_CARRIER + .try_with(Rc::clone) + .unwrap_or_else(|_| Rc::new(RrbVector::new())) +} + +fn empty_map_carrier() -> Rc> { + EMPTY_MAP_CARRIER + .try_with(Rc::clone) + .unwrap_or_else(|_| Rc::new(HamtMap::new())) +} + +const VALUE_DROP_INLINE_DEPTH: u32 = 64; + +thread_local! { + static VALUE_DROP_DEPTH: std::cell::Cell = const { std::cell::Cell::new(0) }; +} + +// TEST-ONLY WORK OBSERVATION: members moved out of a persistent carrier by drop, so the drop's +// cost contract is observed as work rather than time. +#[cfg(test)] +thread_local! { + static DROP_DETACHED_CARRIER_MEMBERS: std::cell::Cell = const { std::cell::Cell::new(0) }; +} + /// The portable form is a plain tree with no sharing, so it is as deep as the value it was taken /// from and needs the same iterative drop (`impl Drop for Value`): a recursive drop of a deep /// portable chain aborted the process (stack overflow) in @@ -1141,15 +1199,19 @@ fn detach_portable_children(value: &mut PortableValue, pending: &mut Vec) { +fn detach_owned_children(value: &mut Value, pending: &mut Vec, detach_carriers: bool) { match value { - Value::List(items) => { + Value::List(items) if detach_carriers => { if let Some(items) = Rc::get_mut(items) { + #[cfg(test)] + DROP_DETACHED_CARRIER_MEMBERS.with(|c| c.set(c.get() + items.len() as u64)); pending.extend(std::mem::take(items)); } } - Value::Map(entries) => { + Value::Map(entries) if detach_carriers => { if let Some(entries) = Rc::get_mut(entries) { + #[cfg(test)] + DROP_DETACHED_CARRIER_MEMBERS.with(|c| c.set(c.get() + entries.len() as u64)); pending.extend(std::mem::take(entries).into_iter().map(|(_, v)| v)); } } @@ -14229,17 +14291,13 @@ macro_rules! v1_algebra_method_arms { arm "method_call.skip" { "skip" } => { let items = expect_list(&$receiver, "skip")?; let n = expect_int($args.first(), "skip")?; - Ok(list_value( - items.iter().skip(n as usize).cloned().collect::>(), - )) + Ok(Value::List(Rc::new(rrb_skip(&items, n)))) }, arm "method_call.take" { "take" } => { let items = expect_list(&$receiver, "take")?; let n = expect_int($args.first(), "take")?; - Ok(list_value( - items.iter().take(n as usize).cloned().collect::>(), - )) + Ok(Value::List(Rc::new(rrb_take(&items, n)))) }, arm "method_call.enumerate" { "enumerate" } => { @@ -23604,7 +23662,7 @@ macro_rules! v1_builtin_arms { arm "free_call.chars_to_string" { "chars_to_string" } => { let cps = match $positional.first().copied() { - Some(v) => free_monoid_to_vec(v).ok_or_else(|| InterpError::TypeError { + Some(v) => free_monoid_items(v).ok_or_else(|| InterpError::TypeError { msg: "chars_to_string expects a list of code points".to_string(), })?, None => { @@ -23621,7 +23679,9 @@ macro_rules! v1_builtin_arms { .max(0) .min(len) .max(start); - let s: String = cps[start as usize..end as usize] + let s: String = cps + .skip(start as usize) + .take((end - start) as usize) .iter() .filter_map(|v| match v { Value::Int(cp) => char::from_u32(*cp as u32), @@ -23632,6 +23692,10 @@ macro_rules! v1_builtin_arms { }, arm "free_call.get" { "get" } => match $positional.as_slice() { + [Value::List(items), idx_val] => { + let idx = expect_int(Some(idx_val), "get")?; + Ok(Some(list_get_at_or_null(items, idx))) + } [list_val, idx_val] if free_monoid_to_vec(list_val).is_some() => { let items = expect_list(list_val, "get")?; let idx = expect_int(Some(idx_val), "get")?; @@ -23712,6 +23776,7 @@ macro_rules! v1_builtin_arms { }, arm "free_call.count" { "count" } => match $positional.first() { + Some(Value::List(items)) => Ok(Some(Value::Int(items.len() as i64))), Some(v) => match free_monoid_to_vec(v) { Some(items) => Ok(Some(Value::Int(items.len() as i64))), None => Ok(None), @@ -26486,6 +26551,183 @@ fn value_to_list_carrier(val: &Value) -> Option<(Rc>, u64)> { } } +/// The members of a code-point (or any FreeMonoid) value as the persistent carrier, sharing a +/// `Value::List`'s tree rather than flattening it. `free_monoid_to_vec` copies all n members, so a +/// consumer that reads a slice through it pays O(n) per read and O(n^2) over a walk; this reads the +/// list form in O(1) and flattens only the Str and Cons forms, which have no shared tree to borrow. +fn free_monoid_items(val: &Value) -> Option>> { + match val { + Value::List(items) => Some(Rc::clone(items)), + other => free_monoid_to_vec(other).map(|items| Rc::new(RrbVector::from(items))), + } +} + +/// std.primitives skip_contract on the persistent carrier: the suffix after `n` members, sharing +/// the receiver's tree, O(log n) rather than a copy of the remainder. A negative `n` keeps the +/// reading the copying form had (`n as usize` saturates past the end), which skips everything. +pub(crate) fn rrb_skip(items: &RrbVector, n: i64) -> RrbVector { + let k = if n < 0 { + items.len() + } else { + (n as usize).min(items.len()) + }; + items.skip(k) +} + +/// std.primitives take_contract on the persistent carrier: the first `n` members, sharing the +/// receiver's tree. A negative `n` keeps every member, as the copying form's saturating cast did. +pub(crate) fn rrb_take(items: &RrbVector, n: i64) -> RrbVector { + let k = if n < 0 { + items.len() + } else { + (n as usize).min(items.len()) + }; + items.take(k) +} + +#[cfg(test)] +mod slice_cost_tests { + use super::*; + use std::cell::Cell; + + thread_local! { + static CLONES: Cell = const { Cell::new(0) }; + } + + /// A code point whose every copy is counted, so a slice's cost is read off the carrier's + /// own work rather than a clock. + #[derive(Debug, PartialEq)] + struct CountedCodePoint(u32); + + impl Clone for CountedCodePoint { + fn clone(&self) -> Self { + CLONES.with(|c| c.set(c.get() + 1)); + CountedCodePoint(self.0) + } + } + + fn clones_during(f: impl FnOnce() -> R) -> (R, u64) { + let before = CLONES.with(|c| c.get()); + let r = f(); + (r, CLONES.with(|c| c.get()) - before) + } + + // Non-ASCII on purpose: the interpreter's string carrier is O(1) per index only on ASCII, + // which is the case a byte-offset decode is NOT linear on. + fn non_ascii_code_points(n: usize) -> RrbVector { + "\u{e9}\u{4e2d}\u{1f600}a" + .chars() + .cycle() + .take(n) + .map(|c| CountedCodePoint(c as u32)) + .collect() + } + + const N: usize = 200_000; + // im's RRB split copies at most one boundary chunk per tree level; this is that bound with + // headroom, and three orders of magnitude below N. + const SLICE_COPY_BUDGET: u64 = 2_048; + + #[test] + fn a_slice_from_the_middle_copies_only_boundary_chunks() { + let cps = non_ascii_code_points(N); + let (slice, copied) = clones_during(|| rrb_take(&rrb_skip(&cps, (N / 2) as i64), 7)); + assert!( + copied <= SLICE_COPY_BUDGET, + "skip+take copied {copied} members of {N}" + ); + let expected: Vec = cps.iter().skip(N / 2).take(7).map(|c| c.0).collect(); + assert_eq!(slice.iter().map(|c| c.0).collect::>(), expected); + } + + // THE RED CONTROL: the copying form these arms had, on the same input and bound. It must + // exceed the budget, or the budget does not discriminate the quadratic. + #[test] + fn the_copying_slice_form_exceeds_the_budget() { + let cps = non_ascii_code_points(N); + let (_, copied) = clones_during(|| { + let rest: RrbVector<_> = cps.iter().skip(N / 2).cloned().collect(); + rest.iter().take(7).cloned().collect::>() + }); + assert!( + copied > SLICE_COPY_BUDGET, + "copying form copied only {copied}" + ); + } + + #[test] + fn negative_and_overlong_counts_keep_the_copying_forms_reading() { + let cps: RrbVector = (0..10).collect(); + let copying_skip = |n: i64| { + cps.iter() + .skip(n as usize) + .cloned() + .collect::>() + }; + let copying_take = |n: i64| { + cps.iter() + .take(n as usize) + .cloned() + .collect::>() + }; + for n in [-3, 0, 4, 10, 11, i64::MAX] { + assert_eq!(rrb_skip(&cps, n), copying_skip(n), "skip {n}"); + assert_eq!(rrb_take(&cps, n), copying_take(n), "take {n}"); + } + } + + fn detached() -> u64 { + DROP_DETACHED_CARRIER_MEMBERS.with(|c| c.get()) + } + + // The slice's drop is part of the slice's cost: a walk drops one slice per step. Below the + // nesting threshold the RRB tree releases its nodes by reference count, so dropping a slice + // of a shared list moves no member; the copying drop moved the whole suffix. + #[test] + fn dropping_a_slice_of_a_shared_list_moves_no_member() { + let base = list_value((0..N as i64).map(Value::Int).collect::>()); + let Value::List(items) = &base else { + unreachable!("list_value builds a list") + }; + let slice = Value::List(Rc::new(rrb_skip(items, (N / 2) as i64))); + let before = detached(); + drop(slice); + assert_eq!(detached() - before, 0, "dropping a shared slice walked it"); + assert_eq!(items.len(), N, "the source survives its slice's drop"); + } + + // THE RED CONTROL: the same drop once nesting is past the threshold, which is the detaching + // path every list drop used to take. It moves the slice's members, so the counter discriminates. + #[test] + fn the_detaching_drop_moves_the_slice_members() { + let base = list_value((0..N as i64).map(Value::Int).collect::>()); + let Value::List(items) = &base else { + unreachable!("list_value builds a list") + }; + let mut slice = Value::List(Rc::new(rrb_skip(items, (N / 2) as i64))); + let before = detached(); + let mut pending = Vec::new(); + detach_owned_children(&mut slice, &mut pending, true); + assert_eq!(detached() - before, (N - N / 2) as u64); + } + + #[test] + fn reading_a_list_value_does_not_flatten_it() { + let list = list_value((0..N as i64).map(Value::Int).collect::>()); + let before = flatten_counters_snapshot(); + let items = free_monoid_items(&list).expect("a list is a free monoid"); + assert_eq!(items.len(), N); + assert_eq!( + flatten_counters_snapshot(), + before, + "free_monoid_items flattened a Value::List" + ); + // RED CONTROL: the flattening read the get / count / chars_to_string arms used. + free_monoid_to_vec(&list).expect("a list is a free monoid"); + assert_eq!(flatten_counters_snapshot().1 - before.1, N as u64); + } +} + fn list_get_at_or_null(items: &RrbVector, idx: i64) -> Value { items.get(idx as usize).cloned().unwrap_or(Value::Null) } diff --git a/src/v1/stage0/src/v1_rt.rs b/src/v1/stage0/src/v1_rt.rs index 7204701cdf4..f4746f4f95b 100644 --- a/src/v1/stage0/src/v1_rt.rs +++ b/src/v1/stage0/src/v1_rt.rs @@ -758,6 +758,118 @@ pub fn list_push(mut list: Vec, item: T) -> Vec { list } +/// std.primitives skip_contract on the persistent carrier: the suffix after `n` members, +/// sharing the receiver's tree in O(log n) instead of copying the remainder, so a walk that +/// slices by offset is O(n log n) rather than quadratic. A negative `n` skips every member, as +/// the copying form's saturating `n as usize` did. +pub fn list_skip(items: &Vec, n: i64) -> Vec { + let k = if n < 0 { + items.len() + } else { + (n as usize).min(items.len()) + }; + items.skip(k) +} + +/// std.primitives take_contract on the persistent carrier: the first `n` members, sharing +/// the receiver's tree. A negative `n` keeps every member, as the copying form did. +pub fn list_take(items: &Vec, n: i64) -> Vec { + let k = if n < 0 { + items.len() + } else { + (n as usize).min(items.len()) + }; + items.take(k) +} + +#[cfg(test)] +mod list_slice_tests { + use super::*; + + thread_local! { + static CLONES: Cell = const { Cell::new(0) }; + } + + /// A code point whose every copy is counted, so a slice's cost is read off the carrier's + /// own work rather than a clock. + #[derive(Debug, PartialEq)] + struct CountedCodePoint(u32); + + impl Clone for CountedCodePoint { + fn clone(&self) -> Self { + CLONES.with(|c| c.set(c.get() + 1)); + CountedCodePoint(self.0) + } + } + + fn clones_during(f: impl FnOnce() -> R) -> (R, u64) { + let before = CLONES.with(|c| c.get()); + let r = f(); + (r, CLONES.with(|c| c.get()) - before) + } + + // Non-ASCII on purpose: emitted char_at / substring over a bare &str are O(offset) here, + // which is the walk the code-point slice replaces. + fn non_ascii_code_points(n: usize) -> Vec { + [0xe9u32, 0x4e2d, 0x1f600, 0x61] + .iter() + .cycle() + .take(n) + .map(|cp| CountedCodePoint(*cp)) + .collect() + } + + const N: usize = 200_000; + // im's RRB split copies at most one boundary chunk per tree level; this is that bound with + // headroom, and three orders of magnitude below N. + const SLICE_COPY_BUDGET: u64 = 2_048; + + #[test] + fn a_slice_from_the_middle_copies_only_boundary_chunks() { + let cps = Rc::new(non_ascii_code_points(N)); + let (slice, copied) = clones_during(|| list_take(&list_skip(&cps, (N / 2) as i64), 7)); + assert!( + copied <= SLICE_COPY_BUDGET, + "skip+take copied {} members of {}", + copied, + N + ); + let expected: std::vec::Vec = cps.iter().skip(N / 2).take(7).map(|c| c.0).collect(); + assert_eq!( + slice.iter().map(|c| c.0).collect::>(), + expected + ); + } + + // THE RED CONTROL: the extdeps.languages.rust.emit skip / take templates this runtime pair + // replaced, spelled as they emitted, on the same input and bound. It must exceed the budget, + // or the budget does not discriminate the quadratic. + #[test] + fn the_copying_template_form_exceeds_the_budget() { + let cps = Rc::new(non_ascii_code_points(N)); + let (_, copied) = clones_during(|| { + let rest = cps.iter().cloned().skip(N / 2).collect::>(); + rest.iter().cloned().take(7).collect::>() + }); + assert!( + copied > SLICE_COPY_BUDGET, + "copying form copied only {}", + copied + ); + } + + #[test] + fn negative_and_overlong_counts_keep_the_copying_forms_reading() { + let xs: Vec = (0..10).collect(); + for n in [-3i64, 0, 4, 10, 11, i64::MAX] { + let copying_skip = xs.iter().cloned().skip(n as usize).collect::>(); + let copying_take = xs.iter().cloned().take(n as usize).collect::>(); + assert_eq!(list_skip(&xs, n), copying_skip, "skip {}", n); + assert_eq!(list_take(&xs, n), copying_take, "take {}", n); + } + } +} + pub fn append(list: Rc>, item: T) -> Vec { let mut v = (*list).clone(); v.push_back(item); diff --git a/src/v1/stage0/src/v1_std_core.rs b/src/v1/stage0/src/v1_std_core.rs index cabb658e176..6e921d69cbc 100644 --- a/src/v1/stage0/src/v1_std_core.rs +++ b/src/v1/stage0/src/v1_std_core.rs @@ -2221,7 +2221,7 @@ pub fn param_node_type_expr(n: Rc) -> Rc { pub fn param_node_default_value(n: Rc) -> Option> { if ((n.children.clone().len() as i64) > 1) { - n.children.clone().iter().cloned().skip(1 as usize).next() + n.children.clone().get((1) as usize).cloned() } else { std::option::Option::None } @@ -2298,7 +2298,7 @@ pub fn field_node_cardinality(n: Rc) -> Cardinality { pub fn field_node_default_value(n: Rc) -> Option> { if ((n.children.clone().len() as i64) > 1) { - n.children.clone().iter().cloned().skip(1 as usize).next() + n.children.clone().get((1) as usize).cloned() } else { std::option::Option::None } @@ -2570,10 +2570,8 @@ pub fn expr_child_at(texpr: Rc, index: i64, role: String) -> Rc { match texpr .children .clone() - .iter() + .get((index.clone()) as usize) .cloned() - .skip(index.clone() as usize) - .next() { Some(v) => v.clone(), std::option::Option::None => make_expr_error_node( @@ -2831,13 +2829,7 @@ pub fn if_then_branch(texpr: Rc) -> Rc { } pub fn if_else_branch(texpr: Rc) -> Option> { - texpr - .children - .clone() - .iter() - .cloned() - .skip(2 as usize) - .next() + texpr.children.clone().get((2) as usize).cloned() } pub fn match_scrutinee(texpr: Rc) -> Rc { @@ -2845,15 +2837,7 @@ pub fn match_scrutinee(texpr: Rc) -> Rc { } pub fn match_arm_nodes(texpr: Rc) -> Rc>> { - Rc::new( - texpr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) + Rc::new(v1_rt::list_skip(&texpr.children.clone(), 1)) } pub fn binop_left(texpr: Rc) -> Rc { @@ -2953,15 +2937,7 @@ pub fn method_receiver(texpr: Rc) -> Rc { } pub fn method_arg_nodes(texpr: Rc) -> Rc>> { - Rc::new( - texpr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) + Rc::new(v1_rt::list_skip(&texpr.children.clone(), 1)) } pub fn expr_method_name_at( @@ -2991,17 +2967,9 @@ pub fn lambda_param_names_at( ) -> Rc> { Rc::new({ let mut __result = Vec::new(); - for n in Rc::new( - texpr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ) - .iter() - .cloned() + for n in Rc::new(v1_rt::list_skip(&texpr.children.clone(), 1)) + .iter() + .cloned() { __result.push(authored_name_at(source_indices.clone(), n.clone())); } @@ -3014,13 +2982,7 @@ pub fn let_value(texpr: Rc) -> Rc { } pub fn let_body(texpr: Rc) -> Option> { - texpr - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .next() + texpr.children.clone().get((1) as usize).cloned() } pub fn let_binding_name_at( @@ -5107,10 +5069,8 @@ pub fn byte_to_line_col(index: Rc, offset: i64) -> LineCol { match index .offsets .clone() - .iter() + .get((v1_rt::int_sub(line.clone(), 2)) as usize) .cloned() - .skip(v1_rt::int_sub(line.clone(), 2) as usize) - .next() { Some(o) => v1_rt::int_add(o.clone(), 1), std::option::Option::None => 0, @@ -5133,10 +5093,8 @@ pub fn source_line_at(index: Rc, line: i64) -> String { match index .offsets .clone() - .iter() + .get((v1_rt::int_sub(line.clone(), 2)) as usize) .cloned() - .skip(v1_rt::int_sub(line.clone(), 2) as usize) - .next() { Some(o) => v1_rt::int_add(o.clone(), 1), std::option::Option::None => src_len.clone(), @@ -5145,10 +5103,8 @@ pub fn source_line_at(index: Rc, line: i64) -> String { let line_end = match index .offsets .clone() - .iter() + .get((v1_rt::int_sub(line.clone(), 1)) as usize) .cloned() - .skip(v1_rt::int_sub(line.clone(), 1) as usize) - .next() { Some(o) => o.clone(), std::option::Option::None => src_len.clone(), diff --git a/src/v1/stage0/src/v1_tests_claim_generic_identity_census.rs b/src/v1/stage0/src/v1_tests_claim_generic_identity_census.rs index 5ad0c3b241f..bc8bf680a15 100644 --- a/src/v1/stage0/src/v1_tests_claim_generic_identity_census.rs +++ b/src/v1/stage0/src/v1_tests_claim_generic_identity_census.rs @@ -1576,15 +1576,7 @@ pub fn gi_owner_argument_arms_discriminate( xs_param.clone(), v1_rt::concat( Rc::new(vec![minted.clone()]), - Rc::new( - xs_param - .children - .clone() - .iter() - .cloned() - .skip(1 as usize) - .collect::>(), - ), + Rc::new(v1_rt::list_skip(&xs_param.children.clone(), 1)), ), ); (((gi_foreign_label_under_owner( diff --git a/src/v1/stage0/src/v1_tests_claim_interpolation_hole_census.rs b/src/v1/stage0/src/v1_tests_claim_interpolation_hole_census.rs index 642898b05bf..9de31d86589 100644 --- a/src/v1/stage0/src/v1_tests_claim_interpolation_hole_census.rs +++ b/src/v1/stage0/src/v1_tests_claim_interpolation_hole_census.rs @@ -418,16 +418,9 @@ pub fn ihc_template_holes(ctx: Rc, template: Rc) -> Rc>(), - )) - .iter() - .cloned(), + (*Rc::new(v1_rt::list_take(&p.children.clone(), 1))) + .iter() + .cloned(), ); } __result diff --git a/src/v2/workflow/floor_route_gap.dag b/src/v2/workflow/floor_route_gap.dag index dbd2cd7655a..b9a0624d094 100644 --- a/src/v2/workflow/floor_route_gap.dag +++ b/src/v2/workflow/floor_route_gap.dag @@ -2098,6 +2098,23 @@ fn floor_route_gap_expectation_chunk_41() -> List { } } +// The two wet witnesses of the workspace source pack: test.claim.seeded_filler_real_execution (the +// seeded filler by real truncate/openssl) and test.claim.workspace_source_pack_archive_real_execution +// (the two-root tar and its digest readback). Each first mints a directory with shell.Mktemp.Dir, +// which the hermetic route has no arm for. Triple enrollment as for chunk_31: these expectations, a +// WetScheduledClaim row per function in local_repo_wet_schedule, and the gunbc.ci_layer_roots +// LocalRepoWetLane rows for both files. +fn floor_route_gap_expectation_chunk_42() -> List { + [ + FloorRouteGapExpectation { identity: "test.claim.seeded_filler_real_execution.a_seeded_filler_is_exactly_its_size_and_reproducible_by_real_execution", operation: "Dir", ground: NoMockResponse {} }, + FloorRouteGapExpectation { identity: "test.claim.seeded_filler_real_execution.two_seeds_give_different_fillers_by_real_execution", operation: "Dir", ground: NoMockResponse {} }, + FloorRouteGapExpectation { identity: "test.claim.seeded_filler_real_execution.a_zero_stand_in_compresses_and_the_filler_does_not_red_control_by_real_execution", operation: "Dir", ground: NoMockResponse {} }, + FloorRouteGapExpectation { identity: "test.claim.workspace_source_pack_archive_real_execution.the_two_root_archive_carries_the_placeholder_and_reads_back_clean_by_real_execution", operation: "Dir", ground: NoMockResponse {} }, + FloorRouteGapExpectation { identity: "test.claim.workspace_source_pack_archive_real_execution.same_size_real_content_at_the_placeholder_path_is_refused_by_digest_by_real_execution", operation: "Dir", ground: NoMockResponse {} }, + FloorRouteGapExpectation { identity: "test.claim.workspace_source_pack_archive_real_execution.one_ordinary_file_and_zero_placeholders_packs_clean_by_real_execution", operation: "Dir", ground: NoMockResponse {} } + ] +} + // The test.claim.scm_repository_save_real_execution_witness identities: the guarded save's real // write over the committed v7 fixture (first effect Filesystem.Write), and a first save from // absence on a throwaway path (first effect shell.Mktemp.DirWithTemplate). Triple enrollment as @@ -2323,7 +2340,7 @@ fn floor_route_gap_expectation_chunk_37() -> List { } fn floor_route_gap_expectation_chunks() -> List> { - Cons { head: floor_route_gap_expectation_chunk_00(), tail: Cons { head: floor_route_gap_expectation_chunk_01(), tail: Cons { head: floor_route_gap_expectation_chunk_02(), tail: Cons { head: floor_route_gap_expectation_chunk_03(), tail: Cons { head: floor_route_gap_expectation_chunk_04(), tail: Cons { head: floor_route_gap_expectation_chunk_05(), tail: Cons { head: floor_route_gap_expectation_chunk_06(), tail: Cons { head: floor_route_gap_expectation_chunk_07(), tail: Cons { head: floor_route_gap_expectation_chunk_08(), tail: Cons { head: floor_route_gap_expectation_chunk_09(), tail: Cons { head: floor_route_gap_expectation_chunk_10(), tail: Cons { head: floor_route_gap_expectation_chunk_11(), tail: Cons { head: floor_route_gap_expectation_chunk_12(), tail: Cons { head: floor_route_gap_expectation_chunk_13(), tail: Cons { head: floor_route_gap_expectation_chunk_14(), tail: Cons { head: floor_route_gap_expectation_chunk_15(), tail: Cons { head: floor_route_gap_expectation_chunk_16(), tail: Cons { head: floor_route_gap_expectation_chunk_17(), tail: Cons { head: floor_route_gap_expectation_chunk_18(), tail: Cons { head: floor_route_gap_expectation_chunk_19(), tail: Cons { head: floor_route_gap_expectation_chunk_20(), tail: Cons { head: floor_route_gap_expectation_chunk_21(), tail: Cons { head: floor_route_gap_expectation_chunk_22(), tail: Cons { head: floor_route_gap_expectation_chunk_23(), tail: Cons { head: floor_route_gap_expectation_chunk_24(), tail: Cons { head: floor_route_gap_expectation_chunk_25(), tail: Cons { head: floor_route_gap_expectation_chunk_26(), tail: Cons { head: floor_route_gap_expectation_chunk_27(), tail: Cons { head: floor_route_gap_expectation_chunk_28(), tail: Cons { head: floor_route_gap_expectation_chunk_29(), tail: Cons { head: floor_route_gap_expectation_chunk_30(), tail: Cons { head: floor_route_gap_expectation_chunk_31(), tail: Cons { head: floor_route_gap_expectation_chunk_32(), tail: Cons { head: floor_route_gap_expectation_chunk_33(), tail: Cons { head: floor_route_gap_expectation_chunk_34(), tail: Cons { head: floor_route_gap_expectation_chunk_35(), tail: Cons { head: floor_route_gap_expectation_chunk_36(), tail: Cons { head: floor_route_gap_expectation_chunk_37(), tail: Cons { head: floor_route_gap_expectation_chunk_38(), tail: Cons { head: floor_route_gap_expectation_chunk_39(), tail: Cons { head: floor_route_gap_expectation_chunk_40(), tail: Cons { head: floor_route_gap_expectation_chunk_41(), tail: Empty {} } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } }} + Cons { head: floor_route_gap_expectation_chunk_00(), tail: Cons { head: floor_route_gap_expectation_chunk_01(), tail: Cons { head: floor_route_gap_expectation_chunk_02(), tail: Cons { head: floor_route_gap_expectation_chunk_03(), tail: Cons { head: floor_route_gap_expectation_chunk_04(), tail: Cons { head: floor_route_gap_expectation_chunk_05(), tail: Cons { head: floor_route_gap_expectation_chunk_06(), tail: Cons { head: floor_route_gap_expectation_chunk_07(), tail: Cons { head: floor_route_gap_expectation_chunk_08(), tail: Cons { head: floor_route_gap_expectation_chunk_09(), tail: Cons { head: floor_route_gap_expectation_chunk_10(), tail: Cons { head: floor_route_gap_expectation_chunk_11(), tail: Cons { head: floor_route_gap_expectation_chunk_12(), tail: Cons { head: floor_route_gap_expectation_chunk_13(), tail: Cons { head: floor_route_gap_expectation_chunk_14(), tail: Cons { head: floor_route_gap_expectation_chunk_15(), tail: Cons { head: floor_route_gap_expectation_chunk_16(), tail: Cons { head: floor_route_gap_expectation_chunk_17(), tail: Cons { head: floor_route_gap_expectation_chunk_18(), tail: Cons { head: floor_route_gap_expectation_chunk_19(), tail: Cons { head: floor_route_gap_expectation_chunk_20(), tail: Cons { head: floor_route_gap_expectation_chunk_21(), tail: Cons { head: floor_route_gap_expectation_chunk_22(), tail: Cons { head: floor_route_gap_expectation_chunk_23(), tail: Cons { head: floor_route_gap_expectation_chunk_24(), tail: Cons { head: floor_route_gap_expectation_chunk_25(), tail: Cons { head: floor_route_gap_expectation_chunk_26(), tail: Cons { head: floor_route_gap_expectation_chunk_27(), tail: Cons { head: floor_route_gap_expectation_chunk_28(), tail: Cons { head: floor_route_gap_expectation_chunk_29(), tail: Cons { head: floor_route_gap_expectation_chunk_30(), tail: Cons { head: floor_route_gap_expectation_chunk_31(), tail: Cons { head: floor_route_gap_expectation_chunk_32(), tail: Cons { head: floor_route_gap_expectation_chunk_33(), tail: Cons { head: floor_route_gap_expectation_chunk_34(), tail: Cons { head: floor_route_gap_expectation_chunk_35(), tail: Cons { head: floor_route_gap_expectation_chunk_36(), tail: Cons { head: floor_route_gap_expectation_chunk_37(), tail: Cons { head: floor_route_gap_expectation_chunk_38(), tail: Cons { head: floor_route_gap_expectation_chunk_39(), tail: Cons { head: floor_route_gap_expectation_chunk_40(), tail: Cons { head: floor_route_gap_expectation_chunk_41(), tail: Cons { head: floor_route_gap_expectation_chunk_42(), tail: Empty {} } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } }} } @@ -2402,8 +2419,12 @@ fn floor_route_gap_roster() -> List { fn floor_route_gap_expectation_chunk_38() -> List { Cons { head: FloorRouteGapExpectation { identity: "test.claim.workspace_network_real_reader_witness_test.real_reader_takes_the_route_the_reader_identity_dictates", operation: "Read", ground: NoMockResponse {} }, + tail: Cons { + head: FloorRouteGapExpectation { identity: "test.claim.workspace_transcript_rate_real_execution.a_real_tree_grows_gains_a_transcript_and_truncates_one_by_real_execution", operation: "Dir", ground: NoMockResponse {} }, + tail: Cons { + head: FloorRouteGapExpectation { identity: "test.claim.workspace_transcript_rate_real_execution.a_root_that_does_not_exist_refuses_by_real_execution", operation: "Dir", ground: NoMockResponse {} }, tail: Empty {} - } + } } } } // THE ADMISSION PARTITION, MODELED ON THE AUTHORITY THAT OWNS THE REGISTER. This is the single diff --git a/src/v2/workflow/local_repo_wet_terminal.dag b/src/v2/workflow/local_repo_wet_terminal.dag index 536251cec66..7f00644060d 100644 --- a/src/v2/workflow/local_repo_wet_terminal.dag +++ b/src/v2/workflow/local_repo_wet_terminal.dag @@ -3050,6 +3050,54 @@ fn local_repo_wet_schedule() -> List { entry: "dag/test/claim/runner/browser_ready_label_real_path_witness_test.dag", function: "revoke_executes_the_dropin_removal_for_real_on_a_scratch_root", expectation: ExpectedToHold {} + }, + WetScheduledClaim { + identity: WitnessIdentity { module_path: "test.claim.workspace_transcript_rate_real_execution", function: "a_real_tree_grows_gains_a_transcript_and_truncates_one_by_real_execution" }, + entry: "dag/test/claim/workspace_transcript_rate_real_execution_witness_test.dag", + function: "a_real_tree_grows_gains_a_transcript_and_truncates_one_by_real_execution", + expectation: ExpectedToHold {} + }, + WetScheduledClaim { + identity: WitnessIdentity { module_path: "test.claim.workspace_transcript_rate_real_execution", function: "a_root_that_does_not_exist_refuses_by_real_execution" }, + entry: "dag/test/claim/workspace_transcript_rate_real_execution_witness_test.dag", + function: "a_root_that_does_not_exist_refuses_by_real_execution", + expectation: ExpectedToHold {} + }, + WetScheduledClaim { + identity: WitnessIdentity { module_path: "test.claim.seeded_filler_real_execution", function: "a_seeded_filler_is_exactly_its_size_and_reproducible_by_real_execution" }, + entry: "dag/test/claim/seeded_filler_real_execution_witness_test.dag", + function: "a_seeded_filler_is_exactly_its_size_and_reproducible_by_real_execution", + expectation: ExpectedToHold {} + }, + WetScheduledClaim { + identity: WitnessIdentity { module_path: "test.claim.seeded_filler_real_execution", function: "two_seeds_give_different_fillers_by_real_execution" }, + entry: "dag/test/claim/seeded_filler_real_execution_witness_test.dag", + function: "two_seeds_give_different_fillers_by_real_execution", + expectation: ExpectedToHold {} + }, + WetScheduledClaim { + identity: WitnessIdentity { module_path: "test.claim.seeded_filler_real_execution", function: "a_zero_stand_in_compresses_and_the_filler_does_not_red_control_by_real_execution" }, + entry: "dag/test/claim/seeded_filler_real_execution_witness_test.dag", + function: "a_zero_stand_in_compresses_and_the_filler_does_not_red_control_by_real_execution", + expectation: ExpectedToHold {} + }, + WetScheduledClaim { + identity: WitnessIdentity { module_path: "test.claim.workspace_source_pack_archive_real_execution", function: "the_two_root_archive_carries_the_placeholder_and_reads_back_clean_by_real_execution" }, + entry: "dag/test/claim/workspace_source_pack_archive_real_execution_witness_test.dag", + function: "the_two_root_archive_carries_the_placeholder_and_reads_back_clean_by_real_execution", + expectation: ExpectedToHold {} + }, + WetScheduledClaim { + identity: WitnessIdentity { module_path: "test.claim.workspace_source_pack_archive_real_execution", function: "same_size_real_content_at_the_placeholder_path_is_refused_by_digest_by_real_execution" }, + entry: "dag/test/claim/workspace_source_pack_archive_real_execution_witness_test.dag", + function: "same_size_real_content_at_the_placeholder_path_is_refused_by_digest_by_real_execution", + expectation: ExpectedToHold {} + }, + WetScheduledClaim { + identity: WitnessIdentity { module_path: "test.claim.workspace_source_pack_archive_real_execution", function: "one_ordinary_file_and_zero_placeholders_packs_clean_by_real_execution" }, + entry: "dag/test/claim/workspace_source_pack_archive_real_execution_witness_test.dag", + function: "one_ordinary_file_and_zero_placeholders_packs_clean_by_real_execution", + expectation: ExpectedToHold {} } ] }